From 690943c335f0e71a00998573eccc54e42d0fa613 Mon Sep 17 00:00:00 2001 From: Edward Cooke Date: Thu, 29 Sep 2022 09:18:57 -0600 Subject: [PATCH] Finalize line endings in cs files to crlf --- .../DeserializingMultipleDocuments.cs | 158 +- YamlDotNet.Samples/Helpers/SampleAttribute.cs | 66 +- .../Helpers/TestOutputHelperExtensions.cs | 66 +- YamlDotNet.Samples/LoadingAYamlStream.cs | 212 +- YamlDotNet.Samples/SerializeObjectGraph.cs | 248 +- .../ValidatingDuringDeserialization.cs | 176 +- YamlDotNet.Test/Core/EmitterTests.cs | 870 +-- YamlDotNet.Test/Core/EmitterTestsHelper.cs | 232 +- YamlDotNet.Test/Core/EventsHelper.cs | 672 +-- YamlDotNet.Test/Core/InsertionQueueTests.cs | 716 +-- YamlDotNet.Test/Core/LookAheadBufferTests.cs | 434 +- YamlDotNet.Test/Core/MarkCursorTests.cs | 82 +- YamlDotNet.Test/Core/ParserTests.cs | 918 +-- YamlDotNet.Test/Core/ScannerTests.cs | 1020 ++-- YamlDotNet.Test/Core/TokenHelper.cs | 330 +- YamlDotNet.Test/Core/YamlExceptionTests.cs | 126 +- YamlDotNet.Test/EnumerableExtensions.cs | 92 +- YamlDotNet.Test/GlobalSuppressions.cs | 58 +- .../Helpers/OrderedDictionaryTests.cs | 218 +- YamlDotNet.Test/Helpers/PortabilityTests.cs | 86 +- .../RepresentationModel/YamlStreamTests.cs | 634 +- .../Serialization/DateTimeConverterTests.cs | 1158 ++-- .../Serialization/DeserializerTest.cs | 628 +- .../Serialization/EmitDefaultValuesTests.cs | 376 +- .../Serialization/GenericTestDictionary.cs | 238 +- .../Serialization/GenericTestList.cs | 222 +- .../Serialization/NamingConventionTests.cs | 170 +- .../Serialization/ObjectFactoryTests.cs | 124 +- .../RepresentationModelSerializationTests.cs | 258 +- .../Serialization/SerializationTestHelper.cs | 1198 ++-- .../Serialization/SerializationTests.cs | 4670 +++++++-------- .../Serialization/TypeConverterTests.cs | 150 +- .../Serialization/YamlCommentTests.cs | 538 +- YamlDotNet.Test/Spec/ParserSpecTests.cs | 234 +- YamlDotNet.Test/Spec/SerializerSpecTests.cs | 268 +- YamlDotNet.Test/Spec/SpecTestData.cs | 262 +- YamlDotNet.Test/StringExtensions.cs | 80 +- YamlDotNet.Test/Yaml.cs | 236 +- YamlDotNet/Core/AnchorName.cs | 152 +- YamlDotNet/Core/AnchorNotFoundException.cs | 116 +- YamlDotNet/Core/CharacterAnalyzer.cs | 334 +- YamlDotNet/Core/Constants.cs | 80 +- YamlDotNet/Core/Cursor.cs | 144 +- YamlDotNet/Core/Emitter.cs | 3862 ++++++------ YamlDotNet/Core/EmitterSettings.cs | 318 +- YamlDotNet/Core/EmitterState.cs | 90 +- YamlDotNet/Core/Events/AnchorAlias.cs | 170 +- YamlDotNet/Core/Events/Comment.cs | 118 +- YamlDotNet/Core/Events/DocumentEnd.cs | 180 +- YamlDotNet/Core/Events/DocumentStart.cs | 256 +- YamlDotNet/Core/Events/EventType.cs | 78 +- .../Core/Events/IParsingEventVisitor.cs | 82 +- YamlDotNet/Core/Events/MappingEnd.cs | 158 +- YamlDotNet/Core/Events/MappingStart.cs | 232 +- YamlDotNet/Core/Events/MappingStyle.cs | 88 +- YamlDotNet/Core/Events/NodeEvent.cs | 144 +- YamlDotNet/Core/Events/ParsingEvent.cs | 136 +- YamlDotNet/Core/Events/Scalar.cs | 300 +- YamlDotNet/Core/Events/SequenceEnd.cs | 158 +- YamlDotNet/Core/Events/SequenceStart.cs | 210 +- YamlDotNet/Core/Events/SequenceStyle.cs | 88 +- YamlDotNet/Core/Events/StreamEnd.cs | 182 +- YamlDotNet/Core/Events/StreamStart.cs | 182 +- .../ForwardAnchorNotSupportedException.cs | 118 +- YamlDotNet/Core/HashCode.cs | 100 +- YamlDotNet/Core/IEmitter.cs | 72 +- YamlDotNet/Core/ILookAheadBuffer.cs | 84 +- YamlDotNet/Core/IParser.cs | 86 +- YamlDotNet/Core/IScanner.cs | 116 +- YamlDotNet/Core/InsertionQueue.cs | 432 +- YamlDotNet/Core/LookAheadBuffer.cs | 342 +- YamlDotNet/Core/Mark.cs | 252 +- .../MaximumRecursionLevelReachedException.cs | 116 +- YamlDotNet/Core/MergingParser.cs | 622 +- YamlDotNet/Core/Parser.cs | 2208 +++---- YamlDotNet/Core/ParserExtensions.cs | 302 +- YamlDotNet/Core/ParserState.cs | 102 +- YamlDotNet/Core/RecursionLevel.cs | 160 +- YamlDotNet/Core/ScalarStyle.cs | 118 +- YamlDotNet/Core/Scanner.cs | 5310 ++++++++--------- YamlDotNet/Core/SemanticErrorException.cs | 116 +- YamlDotNet/Core/SimpleKey.cs | 112 +- YamlDotNet/Core/StringLookAheadBuffer.cs | 122 +- YamlDotNet/Core/SyntaxErrorException.cs | 116 +- YamlDotNet/Core/TagDirectiveCollection.cs | 126 +- YamlDotNet/Core/TagName.cs | 182 +- YamlDotNet/Core/Tokens/Anchor.cs | 124 +- YamlDotNet/Core/Tokens/AnchorAlias.cs | 122 +- YamlDotNet/Core/Tokens/BlockEnd.cs | 94 +- YamlDotNet/Core/Tokens/BlockEntry.cs | 94 +- YamlDotNet/Core/Tokens/BlockMappingStart.cs | 94 +- YamlDotNet/Core/Tokens/BlockSequenceStart.cs | 94 +- YamlDotNet/Core/Tokens/Comment.cs | 118 +- YamlDotNet/Core/Tokens/DocumentEnd.cs | 94 +- YamlDotNet/Core/Tokens/DocumentStart.cs | 94 +- YamlDotNet/Core/Tokens/Error.cs | 80 +- YamlDotNet/Core/Tokens/FlowEntry.cs | 94 +- YamlDotNet/Core/Tokens/FlowMappingEnd.cs | 94 +- YamlDotNet/Core/Tokens/FlowMappingStart.cs | 94 +- YamlDotNet/Core/Tokens/FlowSequenceEnd.cs | 94 +- YamlDotNet/Core/Tokens/FlowSequenceStart.cs | 94 +- YamlDotNet/Core/Tokens/Key.cs | 94 +- YamlDotNet/Core/Tokens/Scalar.cs | 164 +- YamlDotNet/Core/Tokens/StreamEnd.cs | 94 +- YamlDotNet/Core/Tokens/StreamStart.cs | 94 +- YamlDotNet/Core/Tokens/Tag.cs | 134 +- YamlDotNet/Core/Tokens/TagDirective.cs | 236 +- YamlDotNet/Core/Tokens/Token.cs | 100 +- YamlDotNet/Core/Tokens/Value.cs | 94 +- YamlDotNet/Core/Tokens/VersionDirective.cs | 160 +- YamlDotNet/Core/Version.cs | 164 +- YamlDotNet/Core/YamlException.cs | 166 +- YamlDotNet/GlobalSuppressions.cs | 54 +- YamlDotNet/Helpers/ConcurrentObjectPool.cs | 398 +- YamlDotNet/Helpers/ExpressionExtensions.cs | 170 +- .../GenericCollectionToNonGenericAdapter.cs | 250 +- .../GenericDictionaryToNonGenericAdapter.cs | 334 +- YamlDotNet/Helpers/IOrderedDictionary.cs | 110 +- YamlDotNet/Helpers/NumberExtensions.cs | 62 +- YamlDotNet/Helpers/OrderedDictionary.cs | 468 +- YamlDotNet/Helpers/StringBuilderPool.cs | 154 +- YamlDotNet/Helpers/ThrowHelper.cs | 70 +- .../include/DeconstructionExtensions.cs | 64 +- .../exclude/ReadOnlyCollectionExtensions.cs | 76 +- .../include/ConcurrentDictionary.cs | 134 +- .../include/IReadOnlyCollection.cs | 56 +- .../include/IReadOnlyDictionary.cs | 68 +- .../include/IReadOnlyList.cs | 56 +- .../net35+unitysubset3.5/include/Lazy.cs | 188 +- .../net35+unitysubset3.5/include/Linq.cs | 76 +- .../include/ReadOnlyCollectionExtensions.cs | 166 +- .../include/ValueTuple.cs | 100 +- .../net45+net47/include/LazyBuilder.cs | 70 +- .../exclude/CultureInfoAdapter.cs | 84 +- .../exclude/ReflectionExtensions.cs | 332 +- .../include/CultureInfoAdapter.cs | 84 +- .../include/ReflectionExtensions.cs | 578 +- .../exclude/PropertyInfoExtensions.cs | 66 +- .../exclude/StandardRegexOptions.cs | 60 +- .../ExcludeFromCodeCoverageAttribute.cs | 62 +- .../include/PropertyInfoExtensions.cs | 66 +- .../include/StandardRegexOptions.cs | 60 +- .../include/TargetFrameworkAttribute.cs | 76 +- YamlDotNet/Properties/CustomAssemblyInfo.cs | 80 +- .../DocumentLoadingState.cs | 222 +- .../RepresentationModel/EmitterState.cs | 76 +- .../RepresentationModel/IYamlVisitor.cs | 138 +- .../RepresentationModel/LibYamlEventStream.cs | 272 +- .../RepresentationModel/YamlAliasNode.cs | 240 +- .../RepresentationModel/YamlDocument.cs | 428 +- .../RepresentationModel/YamlMappingNode.cs | 852 +-- YamlDotNet/RepresentationModel/YamlNode.cs | 472 +- .../YamlNodeIdentityEqualityComparer.cs | 96 +- .../RepresentationModel/YamlNodeType.cs | 98 +- .../RepresentationModel/YamlScalarNode.cs | 360 +- .../RepresentationModel/YamlSequenceNode.cs | 606 +- YamlDotNet/RepresentationModel/YamlStream.cs | 344 +- YamlDotNet/RepresentationModel/YamlVisitor.cs | 466 +- .../RepresentationModel/YamlVisitorBase.cs | 302 +- YamlDotNet/Serialization/BuilderSkeleton.cs | 652 +- .../Converters/DateTimeConverter.cs | 230 +- .../Serialization/Converters/GuidConverter.cs | 114 +- .../Converters/SystemTypeConverter.cs | 106 +- .../Serialization/DefaultValuesHandling.cs | 104 +- YamlDotNet/Serialization/Deserializer.cs | 298 +- .../Serialization/DeserializerBuilder.cs | 828 +-- .../EmissionPhaseObjectGraphVisitorArgs.cs | 164 +- .../EventEmitters/ChainedEventEmitter.cs | 140 +- .../EventEmitters/JsonEventEmitter.cs | 252 +- .../TypeAssigningEventEmitter.cs | 388 +- .../EventEmitters/WriterEventEmitter.cs | 118 +- YamlDotNet/Serialization/EventInfo.cs | 234 +- YamlDotNet/Serialization/IAliasProvider.cs | 60 +- YamlDotNet/Serialization/IDeserializer.cs | 92 +- YamlDotNet/Serialization/IEventEmitter.cs | 70 +- YamlDotNet/Serialization/INamingConvention.cs | 64 +- YamlDotNet/Serialization/INodeDeserializer.cs | 62 +- YamlDotNet/Serialization/INodeTypeResolver.cs | 80 +- YamlDotNet/Serialization/IObjectDescriptor.cs | 134 +- YamlDotNet/Serialization/IObjectFactory.cs | 78 +- .../IObjectGraphTraversalStrategy.cs | 74 +- .../Serialization/IObjectGraphVisitor.cs | 198 +- .../Serialization/IPropertyDescriptor.cs | 82 +- .../IRegistrationLocationSelectionSyntax.cs | 118 +- YamlDotNet/Serialization/ISerializer.cs | 132 +- YamlDotNet/Serialization/ITypeInspector.cs | 108 +- YamlDotNet/Serialization/ITypeResolver.cs | 66 +- .../Serialization/IValueDeserializer.cs | 64 +- YamlDotNet/Serialization/IValuePromise.cs | 60 +- YamlDotNet/Serialization/IValueSerializer.cs | 62 +- YamlDotNet/Serialization/IYamlConvertible.cs | 134 +- YamlDotNet/Serialization/IYamlSerializable.cs | 86 +- .../Serialization/IYamlTypeConverter.cs | 94 +- .../LazyComponentRegistrationList.cs | 474 +- ...LazyComponentRegistrationListExtensions.cs | 128 +- .../CamelCaseNamingConvention.cs | 92 +- .../HyphenatedNamingConvention.cs | 88 +- .../LowerCaseNamingConvention.cs | 84 +- .../NamingConventions/NullNamingConvention.cs | 88 +- .../PascalCaseNamingConvention.cs | 92 +- .../UnderscoredNamingConvention.cs | 88 +- .../ArrayNodeDeserializer.cs | 238 +- .../CollectionNodeDeserializer.cs | 222 +- .../DictionaryNodeDeserializer.cs | 298 +- .../EnumerableNodeDeserializer.cs | 114 +- .../NodeDeserializers/NullNodeDeserializer.cs | 126 +- .../ObjectNodeDeserializer.cs | 200 +- .../ScalarNodeDeserializer.cs | 848 +-- .../TypeConverterNodeDeserializer.cs | 104 +- .../YamlConvertibleNodeDeserializer.cs | 100 +- .../YamlSerializableNodeDeserializer.cs | 104 +- .../DefaultContainersNodeTypeResolver.cs | 98 +- .../MappingNodeTypeResolver.cs | 122 +- .../RejectUnknownTagsNodeTypeResolver.cs | 78 +- .../NodeTypeResolvers/TagNodeTypeResolver.cs | 96 +- .../TypeNameInTagNodeTypeResolver.cs | 92 +- .../YamlConvertibleTypeResolver.cs | 68 +- .../YamlSerializableTypeResolver.cs | 72 +- YamlDotNet/Serialization/Nothing.cs | 62 +- YamlDotNet/Serialization/ObjectDescriptor.cs | 96 +- .../ObjectFactories/DefaultObjectFactory.cs | 194 +- .../ObjectFactories/LambdaObjectFactory.cs | 86 +- .../FullObjectGraphTraversalStrategy.cs | 514 +- .../RoundtripObjectGraphTraversalStrategy.cs | 106 +- .../ObjectGraphTraversalStrategyFactory.cs | 82 +- .../ObjectGraphVisitors/AnchorAssigner.cs | 210 +- .../AnchorAssigningObjectGraphVisitor.cs | 156 +- .../ChainedObjectGraphVisitor.cs | 152 +- .../CommentsObjectGraphVisitor.cs | 88 +- .../CustomSerializationObjectGraphVisitor.cs | 138 +- .../DefaultExclusiveObjectGraphVisitor.cs | 116 +- .../DefaultValuesObjectGraphVisitor.cs | 182 +- .../EmittingObjectGraphVisitor.cs | 152 +- ...ocessingPhaseObjectGraphVisitorSkeleton.cs | 218 +- .../Serialization/PropertyDescriptor.cs | 150 +- .../Serialization/Schemas/FailsafeSchema.cs | 122 +- YamlDotNet/Serialization/Serializer.cs | 288 +- YamlDotNet/Serialization/SerializerBuilder.cs | 1206 ++-- YamlDotNet/Serialization/StreamFragment.cs | 170 +- YamlDotNet/Serialization/TagMappings.cs | 142 +- .../TypeInspectors/CachedTypeInspector.cs | 94 +- .../TypeInspectors/CompositeTypeInspector.cs | 102 +- .../NamingConventionTypeInspector.cs | 114 +- ...dableAndWritablePropertiesTypeInspector.cs | 92 +- .../ReadableFieldsTypeInspector.cs | 174 +- .../ReadablePropertiesTypeInspector.cs | 202 +- .../TypeInspectors/TypeInspectorSkeleton.cs | 124 +- .../WritablePropertiesTypeInspector.cs | 204 +- .../TypeResolvers/DynamicTypeResolver.cs | 72 +- .../TypeResolvers/StaticTypeResolver.cs | 72 +- .../Utilities/IPostDeserializationCallback.cs | 64 +- .../Utilities/ObjectAnchorCollection.cs | 150 +- .../Utilities/ReflectionUtility.cs | 108 +- .../Utilities/SerializerState.cs | 134 +- .../Utilities/StringExtensions.cs | 158 +- .../Serialization/Utilities/TypeConverter.cs | 536 +- .../AliasValueDeserializer.cs | 294 +- .../NodeValueDeserializer.cs | 178 +- .../Serialization/YamlAttributeOverrides.cs | 396 +- .../YamlAttributeOverridesInspector.cs | 220 +- .../YamlAttributesTypeInspector.cs | 140 +- YamlDotNet/Serialization/YamlFormatter.cs | 150 +- .../Serialization/YamlIgnoreAttribute.cs | 68 +- .../Serialization/YamlMemberAttribute.cs | 190 +- tools/build/AutoNumberToStringConverter.cs | 88 +- tools/build/BuildDefinition.cs | 1184 ++-- tools/build/GitHubApiModels.cs | 30 +- tools/build/Program.cs | 674 +-- tools/build/TwitterProvider.cs | 272 +- 269 files changed, 35296 insertions(+), 35296 deletions(-) diff --git a/YamlDotNet.Samples/DeserializingMultipleDocuments.cs b/YamlDotNet.Samples/DeserializingMultipleDocuments.cs index 8350fe933..f1af6c30c 100644 --- a/YamlDotNet.Samples/DeserializingMultipleDocuments.cs +++ b/YamlDotNet.Samples/DeserializingMultipleDocuments.cs @@ -1,79 +1,79 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using System.IO; -using Xunit.Abstractions; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Samples.Helpers; -using YamlDotNet.Serialization; - -namespace YamlDotNet.Samples -{ - public class DeserializingMultipleDocuments - { - private readonly ITestOutputHelper output; - - public DeserializingMultipleDocuments(ITestOutputHelper output) - { - this.output = output; - } - - [Sample( - DisplayName = "Deserializing multiple documents", - Description = "Explains how to load multiple YAML documents from a stream." - )] - public void Main() - { - var input = new StringReader(Document); - - var deserializer = new DeserializerBuilder().Build(); - - var parser = new Parser(input); - - // Consume the stream start event "manually" - parser.Consume(); - - while (parser.Accept(out var _)) - { - // Deserialize the document - var doc = deserializer.Deserialize>(parser); - - output.WriteLine("## Document"); - foreach (var item in doc) - { - output.WriteLine(item); - } - } - } - - private const string Document = @"--- -- Prisoner -- Goblet -- Phoenix ---- -- Memoirs -- Snow -- Ghost -..."; - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using System.IO; +using Xunit.Abstractions; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Samples.Helpers; +using YamlDotNet.Serialization; + +namespace YamlDotNet.Samples +{ + public class DeserializingMultipleDocuments + { + private readonly ITestOutputHelper output; + + public DeserializingMultipleDocuments(ITestOutputHelper output) + { + this.output = output; + } + + [Sample( + DisplayName = "Deserializing multiple documents", + Description = "Explains how to load multiple YAML documents from a stream." + )] + public void Main() + { + var input = new StringReader(Document); + + var deserializer = new DeserializerBuilder().Build(); + + var parser = new Parser(input); + + // Consume the stream start event "manually" + parser.Consume(); + + while (parser.Accept(out var _)) + { + // Deserialize the document + var doc = deserializer.Deserialize>(parser); + + output.WriteLine("## Document"); + foreach (var item in doc) + { + output.WriteLine(item); + } + } + } + + private const string Document = @"--- +- Prisoner +- Goblet +- Phoenix +--- +- Memoirs +- Snow +- Ghost +..."; + } +} diff --git a/YamlDotNet.Samples/Helpers/SampleAttribute.cs b/YamlDotNet.Samples/Helpers/SampleAttribute.cs index d786bde52..0fb5c003e 100644 --- a/YamlDotNet.Samples/Helpers/SampleAttribute.cs +++ b/YamlDotNet.Samples/Helpers/SampleAttribute.cs @@ -1,33 +1,33 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Xunit; - -namespace YamlDotNet.Samples.Helpers -{ - /// - /// Marks a test as being a code sample. - /// - public class SampleAttribute : FactAttribute - { - public string Description { get; set; } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using Xunit; + +namespace YamlDotNet.Samples.Helpers +{ + /// + /// Marks a test as being a code sample. + /// + public class SampleAttribute : FactAttribute + { + public string Description { get; set; } + } +} diff --git a/YamlDotNet.Samples/Helpers/TestOutputHelperExtensions.cs b/YamlDotNet.Samples/Helpers/TestOutputHelperExtensions.cs index d872d9f9a..9a7cfd8e7 100644 --- a/YamlDotNet.Samples/Helpers/TestOutputHelperExtensions.cs +++ b/YamlDotNet.Samples/Helpers/TestOutputHelperExtensions.cs @@ -1,33 +1,33 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Xunit.Abstractions; - -namespace YamlDotNet.Samples.Helpers -{ - public static class TestOutputHelperExtensions - { - public static void WriteLine(this ITestOutputHelper output) - { - output.WriteLine(string.Empty); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using Xunit.Abstractions; + +namespace YamlDotNet.Samples.Helpers +{ + public static class TestOutputHelperExtensions + { + public static void WriteLine(this ITestOutputHelper output) + { + output.WriteLine(string.Empty); + } + } +} diff --git a/YamlDotNet.Samples/LoadingAYamlStream.cs b/YamlDotNet.Samples/LoadingAYamlStream.cs index d5d210892..a546c3ee3 100644 --- a/YamlDotNet.Samples/LoadingAYamlStream.cs +++ b/YamlDotNet.Samples/LoadingAYamlStream.cs @@ -1,106 +1,106 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.IO; -using Xunit.Abstractions; -using YamlDotNet.RepresentationModel; -using YamlDotNet.Samples.Helpers; - -namespace YamlDotNet.Samples -{ - public class LoadingAYamlStream - { - private readonly ITestOutputHelper output; - - public LoadingAYamlStream(ITestOutputHelper output) - { - this.output = output; - } - - [Sample( - DisplayName = "Loading a YAML Stream", - Description = "Explains how to load YAML using the representation model." - )] - public void Main() - { - // Setup the input - var input = new StringReader(Document); - - // Load the stream - var yaml = new YamlStream(); - yaml.Load(input); - - // Examine the stream - var mapping = - (YamlMappingNode)yaml.Documents[0].RootNode; - - foreach (var entry in mapping.Children) - { - output.WriteLine(((YamlScalarNode)entry.Key).Value); - } - - // List all the items - var items = (YamlSequenceNode)mapping.Children[new YamlScalarNode("items")]; - foreach (YamlMappingNode item in items) - { - output.WriteLine( - "{0}\t{1}", - item.Children[new YamlScalarNode("part_no")], - item.Children[new YamlScalarNode("descrip")] - ); - } - } - - private const string Document = @"--- - receipt: Oz-Ware Purchase Invoice - date: 2007-08-06 - customer: - given: Dorothy - family: Gale - - items: - - part_no: A4786 - descrip: Water Bucket (Filled) - price: 1.47 - quantity: 4 - - - part_no: E1628 - descrip: High Heeled ""Ruby"" Slippers - price: 100.27 - quantity: 1 - - bill-to: &id001 - street: | - 123 Tornado Alley - Suite 16 - city: East Westville - state: KS - - ship-to: *id001 - - specialDelivery: > - Follow the Yellow Brick - Road to the Emerald City. - Pay no attention to the - man behind the curtain. -..."; - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.IO; +using Xunit.Abstractions; +using YamlDotNet.RepresentationModel; +using YamlDotNet.Samples.Helpers; + +namespace YamlDotNet.Samples +{ + public class LoadingAYamlStream + { + private readonly ITestOutputHelper output; + + public LoadingAYamlStream(ITestOutputHelper output) + { + this.output = output; + } + + [Sample( + DisplayName = "Loading a YAML Stream", + Description = "Explains how to load YAML using the representation model." + )] + public void Main() + { + // Setup the input + var input = new StringReader(Document); + + // Load the stream + var yaml = new YamlStream(); + yaml.Load(input); + + // Examine the stream + var mapping = + (YamlMappingNode)yaml.Documents[0].RootNode; + + foreach (var entry in mapping.Children) + { + output.WriteLine(((YamlScalarNode)entry.Key).Value); + } + + // List all the items + var items = (YamlSequenceNode)mapping.Children[new YamlScalarNode("items")]; + foreach (YamlMappingNode item in items) + { + output.WriteLine( + "{0}\t{1}", + item.Children[new YamlScalarNode("part_no")], + item.Children[new YamlScalarNode("descrip")] + ); + } + } + + private const string Document = @"--- + receipt: Oz-Ware Purchase Invoice + date: 2007-08-06 + customer: + given: Dorothy + family: Gale + + items: + - part_no: A4786 + descrip: Water Bucket (Filled) + price: 1.47 + quantity: 4 + + - part_no: E1628 + descrip: High Heeled ""Ruby"" Slippers + price: 100.27 + quantity: 1 + + bill-to: &id001 + street: | + 123 Tornado Alley + Suite 16 + city: East Westville + state: KS + + ship-to: *id001 + + specialDelivery: > + Follow the Yellow Brick + Road to the Emerald City. + Pay no attention to the + man behind the curtain. +..."; + } +} diff --git a/YamlDotNet.Samples/SerializeObjectGraph.cs b/YamlDotNet.Samples/SerializeObjectGraph.cs index b73b57446..46423078e 100644 --- a/YamlDotNet.Samples/SerializeObjectGraph.cs +++ b/YamlDotNet.Samples/SerializeObjectGraph.cs @@ -1,124 +1,124 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using Xunit.Abstractions; -using YamlDotNet.Samples.Helpers; -using YamlDotNet.Serialization; - -namespace YamlDotNet.Samples -{ - public class SerializeObjectGraph - { - private readonly ITestOutputHelper output; - - public SerializeObjectGraph(ITestOutputHelper output) - { - this.output = output; - } - - [Sample( - DisplayName = "Serializing an object graph", - Description = "Shows how to convert an object to its YAML representation." - )] - public void Main() - { - var address = new Address - { - street = "123 Tornado Alley\nSuite 16", - city = "East Westville", - state = "KS" - }; - - var receipt = new Receipt - { - receipt = "Oz-Ware Purchase Invoice", - date = new DateTime(2007, 8, 6), - customer = new Customer - { - given = "Dorothy", - family = "Gale" - }, - items = new Item[] - { - new Item - { - part_no = "A4786", - descrip = "Water Bucket (Filled)", - price = 1.47M, - quantity = 4 - }, - new Item - { - part_no = "E1628", - descrip = "High Heeled \"Ruby\" Slippers", - price = 100.27M, - quantity = 1 - } - }, - bill_to = address, - ship_to = address, - specialDelivery = "Follow the Yellow Brick\n" + - "Road to the Emerald City.\n" + - "Pay no attention to the\n" + - "man behind the curtain." - }; - - var serializer = new SerializerBuilder().Build(); - var yaml = serializer.Serialize(receipt); - output.WriteLine(yaml); - } - } - -#pragma warning disable IDE1006 // Naming Styles - public class Address - { - public string street { get; set; } - public string city { get; set; } - public string state { get; set; } - } - - public class Receipt - { - public string receipt { get; set; } - public DateTime date { get; set; } - public Customer customer { get; set; } - public Item[] items { get; set; } - public Address bill_to { get; set; } - public Address ship_to { get; set; } - public string specialDelivery { get; set; } - } - - public class Customer - { - public string given { get; set; } - public string family { get; set; } - } - - public class Item - { - public string part_no { get; set; } - public string descrip { get; set; } - public decimal price { get; set; } - public int quantity { get; set; } - } -#pragma warning restore IDE1006 // Naming Styles -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using Xunit.Abstractions; +using YamlDotNet.Samples.Helpers; +using YamlDotNet.Serialization; + +namespace YamlDotNet.Samples +{ + public class SerializeObjectGraph + { + private readonly ITestOutputHelper output; + + public SerializeObjectGraph(ITestOutputHelper output) + { + this.output = output; + } + + [Sample( + DisplayName = "Serializing an object graph", + Description = "Shows how to convert an object to its YAML representation." + )] + public void Main() + { + var address = new Address + { + street = "123 Tornado Alley\nSuite 16", + city = "East Westville", + state = "KS" + }; + + var receipt = new Receipt + { + receipt = "Oz-Ware Purchase Invoice", + date = new DateTime(2007, 8, 6), + customer = new Customer + { + given = "Dorothy", + family = "Gale" + }, + items = new Item[] + { + new Item + { + part_no = "A4786", + descrip = "Water Bucket (Filled)", + price = 1.47M, + quantity = 4 + }, + new Item + { + part_no = "E1628", + descrip = "High Heeled \"Ruby\" Slippers", + price = 100.27M, + quantity = 1 + } + }, + bill_to = address, + ship_to = address, + specialDelivery = "Follow the Yellow Brick\n" + + "Road to the Emerald City.\n" + + "Pay no attention to the\n" + + "man behind the curtain." + }; + + var serializer = new SerializerBuilder().Build(); + var yaml = serializer.Serialize(receipt); + output.WriteLine(yaml); + } + } + +#pragma warning disable IDE1006 // Naming Styles + public class Address + { + public string street { get; set; } + public string city { get; set; } + public string state { get; set; } + } + + public class Receipt + { + public string receipt { get; set; } + public DateTime date { get; set; } + public Customer customer { get; set; } + public Item[] items { get; set; } + public Address bill_to { get; set; } + public Address ship_to { get; set; } + public string specialDelivery { get; set; } + } + + public class Customer + { + public string given { get; set; } + public string family { get; set; } + } + + public class Item + { + public string part_no { get; set; } + public string descrip { get; set; } + public decimal price { get; set; } + public int quantity { get; set; } + } +#pragma warning restore IDE1006 // Naming Styles +} diff --git a/YamlDotNet.Samples/ValidatingDuringDeserialization.cs b/YamlDotNet.Samples/ValidatingDuringDeserialization.cs index 0d55fb3bf..95f590b31 100644 --- a/YamlDotNet.Samples/ValidatingDuringDeserialization.cs +++ b/YamlDotNet.Samples/ValidatingDuringDeserialization.cs @@ -1,88 +1,88 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.ComponentModel.DataAnnotations; -using System.IO; -using Xunit; -using YamlDotNet.Core; -using YamlDotNet.Samples.Helpers; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NodeDeserializers; - -namespace YamlDotNet.Samples -{ - public class ValidatingDuringDeserialization - { - // First, we'll implement a new INodeDeserializer - // that will decorate another INodeDeserializer with validation: - public class ValidatingNodeDeserializer : INodeDeserializer - { - private readonly INodeDeserializer nodeDeserializer; - - public ValidatingNodeDeserializer(INodeDeserializer nodeDeserializer) - { - this.nodeDeserializer = nodeDeserializer; - } - - public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object value) - { - if (nodeDeserializer.Deserialize(parser, expectedType, nestedObjectDeserializer, out value)) - { - var context = new ValidationContext(value, null, null); - Validator.ValidateObject(value, context, true); - return true; - } - return false; - } - } - - [Sample( - DisplayName = "Validating during deserialization", - Description = @" - By manipulating the list of node deserializers, - it is easy to add behavior to the deserializer. - This example shows how to validate the objects as they are deserialized. - " - )] - public void Main() - { - // Then we wrap the existing ObjectNodeDeserializer - // with our ValidatingNodeDeserializer: - var deserializer = new DeserializerBuilder() - .WithNodeDeserializer(inner => new ValidatingNodeDeserializer(inner), s => s.InsteadOf()) - .Build(); - - // This will fail with a validation exception - var ex = Assert.Throws(() => - deserializer.Deserialize(new StringReader(@"Name: ~")) - ); - - Assert.IsType(ex.InnerException); - } - } - - public class Data - { - [Required] - public string Name { get; set; } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.ComponentModel.DataAnnotations; +using System.IO; +using Xunit; +using YamlDotNet.Core; +using YamlDotNet.Samples.Helpers; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NodeDeserializers; + +namespace YamlDotNet.Samples +{ + public class ValidatingDuringDeserialization + { + // First, we'll implement a new INodeDeserializer + // that will decorate another INodeDeserializer with validation: + public class ValidatingNodeDeserializer : INodeDeserializer + { + private readonly INodeDeserializer nodeDeserializer; + + public ValidatingNodeDeserializer(INodeDeserializer nodeDeserializer) + { + this.nodeDeserializer = nodeDeserializer; + } + + public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object value) + { + if (nodeDeserializer.Deserialize(parser, expectedType, nestedObjectDeserializer, out value)) + { + var context = new ValidationContext(value, null, null); + Validator.ValidateObject(value, context, true); + return true; + } + return false; + } + } + + [Sample( + DisplayName = "Validating during deserialization", + Description = @" + By manipulating the list of node deserializers, + it is easy to add behavior to the deserializer. + This example shows how to validate the objects as they are deserialized. + " + )] + public void Main() + { + // Then we wrap the existing ObjectNodeDeserializer + // with our ValidatingNodeDeserializer: + var deserializer = new DeserializerBuilder() + .WithNodeDeserializer(inner => new ValidatingNodeDeserializer(inner), s => s.InsteadOf()) + .Build(); + + // This will fail with a validation exception + var ex = Assert.Throws(() => + deserializer.Deserialize(new StringReader(@"Name: ~")) + ); + + Assert.IsType(ex.InnerException); + } + } + + public class Data + { + [Required] + public string Name { get; set; } + } +} diff --git a/YamlDotNet.Test/Core/EmitterTests.cs b/YamlDotNet.Test/Core/EmitterTests.cs index d4a099d64..5633ff3a5 100644 --- a/YamlDotNet.Test/Core/EmitterTests.cs +++ b/YamlDotNet.Test/Core/EmitterTests.cs @@ -1,435 +1,435 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using FluentAssertions; -using Xunit; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.RepresentationModel; - -namespace YamlDotNet.Test.Core -{ - public class EmitterTests : EmitterTestsHelper - { - [Theory] - [InlineData("01-directives.yaml")] - [InlineData("02-scalar-in-imp-doc.yaml")] - [InlineData("03-scalar-in-exp-doc.yaml")] - [InlineData("04-scalars-in-multi-docs.yaml")] - [InlineData("05-circular-sequence.yaml")] - [InlineData("06-float-tag.yaml")] - [InlineData("07-scalar-styles.yaml")] - [InlineData("08-flow-sequence.yaml")] - [InlineData("09-flow-mapping.yaml")] - [InlineData("10-mixed-nodes-in-sequence.yaml")] - [InlineData("11-mixed-nodes-in-mapping.yaml")] - [InlineData("12-compact-sequence.yaml")] - [InlineData("13-compact-mapping.yaml")] - [InlineData("14-mapping-wo-indent.yaml")] - public void CompareOriginalAndEmittedText(string filename) - { - var stream = Yaml.ReaderFrom(filename); - - var originalEvents = ParsingEventsOf(stream.ReadToEnd()); - var emittedText = EmittedTextFrom(originalEvents); - var emittedEvents = ParsingEventsOf(emittedText); - - emittedEvents.ShouldAllBeEquivalentTo(originalEvents, - opt => opt.Excluding(@event => @event.Start) - .Excluding(@event => @event.End) - .Excluding((ParsingEvent @event) => ((DocumentEnd)@event).IsImplicit)); - } - - private IList ParsingEventsOf(string text) - { - var parser = new Parser(new StringReader(text)); - return EnumerationOf(parser).ToList(); - } - - private IEnumerable EnumerationOf(IParser parser) - { - while (parser.MoveNext()) - { - yield return parser.Current; - } - } - - [Fact] - public void PlainScalarCanBeFollowedByImplicitDocument() - { - var events = StreamOf( - DocumentWith(PlainScalar("test")), - DocumentWith(PlainScalar("test"))); - - var yaml = EmittedTextFrom(events); - - yaml.Should().Contain(Lines("test", "--- test")); - } - - [Fact] - public void PlainScalarCanBeFollowedByDocumentWithVersion() - { - var events = StreamOf( - DocumentWith(PlainScalar("test")), - DocumentWithVersion(PlainScalar("test"))); - - var yaml = EmittedTextFrom(events); - - yaml.Should().Contain(Lines("test", "...", "%YAML 1.1", "--- test")); - } - - [Fact] - public void PlainScalarCanBeFollowedByDocumentWithDefaultTags() - { - var events = StreamOf( - DocumentWith(PlainScalar("test")), - DocumentWithDefaultTags(PlainScalar("test"))); - - var yaml = EmittedTextFrom(events); - - yaml.Should().Contain(Lines("test", "--- test")); - } - - [Fact] - public void PlainScalarCanBeFollowedByDocumentWithCustomTags() - { - var events = StreamOf( - DocumentWith(PlainScalar("test")), - DocumentWithCustomTags(PlainScalar("test"))); - - var yaml = EmittedTextFrom(events); - - yaml.Should().Contain(Lines("test", "...", FooTag, ExTag, ExExTag, "--- test")); - } - - [Fact] - public void BlockCanBeFollowedByImplicitDocument() - { - var events = StreamOf( - DocumentWith(SequenceWith(SingleQuotedScalar("test"))), - DocumentWith(PlainScalar("test"))); - - var yaml = EmittedTextFrom(events); - - yaml.Should().Contain(Lines("- 'test'", "--- test")); - } - - [Fact] - public void BlockCanBeFollowedByDocumentWithVersion() - { - var events = StreamOf( - DocumentWith(SequenceWith(SingleQuotedScalar("test"))), - DocumentWithVersion(PlainScalar("test"))); - - var yaml = EmittedTextFrom(events); - - yaml.Should().Contain(Lines("- 'test'", "...", "%YAML 1.1", "--- test")); - } - - [Fact] - public void BlockCanBeFollowedByDocumentWithDefaultTags() - { - var events = StreamOf( - DocumentWith(SequenceWith(SingleQuotedScalar("test"))), - DocumentWithDefaultTags(PlainScalar("test"))); - - var yaml = EmittedTextFrom(events); - - yaml.Should().Contain(Lines("- 'test'", "--- test")); - } - - [Fact] - public void BlockCanBeFollowedByDocumentWithCustomTags() - { - var events = StreamOf( - DocumentWith(SequenceWith(SingleQuotedScalar("test"))), - DocumentWithCustomTags(PlainScalar("test"))); - - var yaml = EmittedTextFrom(events); - - yaml.Should().Contain(Lines("- 'test'", "...", FooTag, ExTag, ExExTag, "--- test")); - } - - [Theory] - [InlineData("test", ">-\r\n test\r\n")] // No indentation indicator when no indent. - [InlineData(" test", ">2-\r\n test\r\n")] - public void BlockStyleGeneratesIndentationIndicator(string input, string expected) - { - var events = StreamOf( - DocumentWith(FoldedScalar(input))); - - var yaml = EmittedTextFrom(events); - - yaml.Should().Be(expected.NormalizeNewLines()); - } - - [Theory] - [InlineData("LF hello\nworld")] - [InlineData("CRLF hello\r\nworld")] - public void FoldedStyleDoesNotLooseCharacters(string text) - { - var events = SequenceWith(FoldedScalar(text)); - - var yaml = EmittedTextFrom(StreamedDocumentWith(events)); - - yaml.Should().Contain("world"); - } - - [Fact] - public void FoldedStyleIsSelectedWhenNewLinesAreFoundInLiteral() - { - var events = SequenceWith(Scalar("hello\nworld").ImplicitPlain); - - var yaml = EmittedTextFrom(StreamedDocumentWith(events)); - - yaml.Should().Contain(">"); - } - - - [Fact] - [Trait("motive", "pr #540")] - public void AllowBlockStyleInMultilineScalarsWithTrailingSpaces() - { - var events = SequenceWith(Scalar("hello \nworld").ImplicitPlain); - - var yaml = EmittedTextFrom(StreamedDocumentWith(events)); - - yaml.Should().Contain("\n"); - } - - - [Fact] - public void FoldedStyleDoesNotGenerateExtraLineBreaks() - { - var events = SequenceWith(FoldedScalar("hello\nworld")); - - var yaml = EmittedTextFrom(StreamedDocumentWith(events)); - - // Todo: Why involve the rep. model when testing the Emitter? Can we match using a regex? - var stream = new YamlStream(); - stream.Load(new StringReader(yaml)); - var sequence = (YamlSequenceNode)stream.Documents[0].RootNode; - var scalar = (YamlScalarNode)sequence.Children[0]; - - scalar.Value.Should().Be("hello\nworld"); - } - - [Fact] - public void FoldedStyleDoesNotCollapseLineBreaks() - { - var events = SequenceWith(FoldedScalar(">+\n")); - - var yaml = EmittedTextFrom(StreamedDocumentWith(events)); - - var stream = new YamlStream(); - stream.Load(new StringReader(yaml)); - var sequence = (YamlSequenceNode)stream.Documents[0].RootNode; - var scalar = (YamlScalarNode)sequence.Children[0]; - - scalar.Value.Should().Be(">+\n"); - } - - [Fact] - [Trait("motive", "issue #39")] - public void FoldedStylePreservesNewLines() - { - var input = "id: 0\nPayload:\n X: 5\n Y: 6\n"; - var events = MappingWith( - Scalar("Payload").ImplicitPlain, - FoldedScalar(input)); - - var yaml = EmittedTextFrom(StreamedDocumentWith(events)); - - var stream = new YamlStream(); - stream.Load(new StringReader(yaml)); - - var mapping = (YamlMappingNode)stream.Documents[0].RootNode; - var value = (YamlScalarNode)mapping.Children.First().Value; - - value.Value.Should().Be(input); - } - - [Fact] - public void CommentsAreEmittedCorrectly() - { - var events = SequenceWith( - StandaloneComment("Top comment"), - StandaloneComment("Second line"), - Scalar("first").ImplicitPlain, - InlineComment("The first value"), - Scalar("second").ImplicitPlain, - InlineComment("The second value"), - StandaloneComment("Bottom comment") - ); - - var yaml = EmittedTextFrom(StreamedDocumentWith(events)); - - yaml.Should() - .Contain("# Top comment") - .And.Contain("# Second line") - .And.NotContain("# Top comment # Second line") - .And.Contain("first # The first value") - .And.Contain("second # The second value") - .And.Contain("# Bottom comment"); - } - - [Fact] - public void ACommentAsTheFirstEventAddsANewLine() - { - var events = new ParsingEvent[] - { - StandaloneComment("Top comment"), - Scalar("first").ImplicitPlain, - }; - - var yaml = EmittedTextFrom(StreamedDocumentWith(events)); - - yaml.Should() - .Contain("# Top comment") - .And.Contain("first") - .And.NotContain("# Top commentfirst"); - } - - [Theory] - [InlineData("Гранит", 28595)] // Cyrillic (ISO) - [InlineData("ГÀƊȽ˱ώҔׂۋᵷẁό₩וּﺪﺸﻸﭧ╬♫₹Ὰỗ᷁ݭ٭ӢР͞ʓLjĄë0", 65001)] // UTF-8 - public void UnicodeInScalarsCanBeSingleQuotedWhenOutputEncodingSupportsIt(string text, int codePage) - { - var document = StreamedDocumentWith( - SequenceWith( - SingleQuotedScalar(text) - ) - ); - var buffer = new MemoryStream(); -#if (NETCOREAPP2_1_OR_GREATER) - // Code pages such as Cyrillic are not recognized by default in - // .NET Core. We need to register this provider. - // https://msdn.microsoft.com/en-us/library/mt643899(v=vs.110).aspx#Remarks - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); -#endif - var encoding = Encoding.GetEncoding(codePage); - using (var writer = new StreamWriter(buffer, encoding)) - { - var emitter = new Emitter(writer, 2, int.MaxValue, false); - foreach (var evt in document) - { - emitter.Emit(evt); - } - } - - var yaml = encoding.GetString(buffer.ToArray()); - - yaml.Should() - .Contain("'" + text + "'"); - } - - [Fact] - public void EmptyStringsAreQuoted() - { - var events = SequenceWith( - Scalar(string.Empty).ImplicitPlain - ); - - var yaml = EmittedTextFrom(StreamedDocumentWith(events)); - yaml.Should() - .Contain("- ''"); - } - - [Theory] - [InlineData("b-carriage-return,b-line-feed\r\nlll", "b-carriage-return,b-line-feed\nlll")] - [InlineData("b-carriage-return,b-line-feed\r\n\r\nlll", "b-carriage-return,b-line-feed\n\nlll")] - [InlineData("b-carriage-return\rlll", "b-carriage-return\nlll")] - [InlineData("b-line-feed\nlll", "b-line-feed\nlll")] - [InlineData("b-next-line\x85lll", "b-next-line\nlll")] - [InlineData("b-line-separator\x2028lll", "b-line-separator\x2028lll")] - [InlineData("b-paragraph-separator\x2029lll", "b-paragraph-separator\x2029lll")] - public void NewLinesAreNotDuplicatedWhenEmitted(string input, string expected) - { - var yaml = EmittedTextFrom(StreamOf(DocumentWith( - LiteralScalar(input) - ))); - - AssertSequenceOfEventsFrom(Yaml.ParserForText(yaml), - StreamStart, - DocumentStart(Implicit), - LiteralScalar(expected), - DocumentEnd(Implicit), - StreamEnd); - } - - [Theory] - [InlineData("b-carriage-return,b-line-feed\r\nlll", "b-carriage-return,b-line-feed\nlll")] - [InlineData("b-carriage-return,b-line-feed\r\n\r\nlll", "b-carriage-return,b-line-feed\n\nlll")] - [InlineData("b-carriage-return\rlll", "b-carriage-return\nlll")] - [InlineData("b-line-feed\nlll", "b-line-feed\nlll")] - [InlineData("b-next-line\x85lll", "b-next-line\nlll")] - [InlineData("b-line-separator\x2028lll", "b-line-separator\x2028lll")] - [InlineData("b-paragraph-separator\x2029lll", "b-paragraph-separator\x2029lll")] - public void NewLinesAreNotDuplicatedWhenEmittedInFoldedScalar(string input, string expected) - { - var yaml = EmittedTextFrom(StreamOf(DocumentWith( - FoldedScalar(input) - ))); - - AssertSequenceOfEventsFrom(Yaml.ParserForText(yaml), - StreamStart, - DocumentStart(Implicit), - FoldedScalar(expected), - DocumentEnd(Implicit), - StreamEnd); - } - - [Theory] - [InlineData("'.'test")] - [InlineData("'")] - [InlineData("'.'")] - [InlineData("'test")] - [InlineData("'test'")] - public void SingleQuotesAreDoubleQuoted(string input) - { - var events = StreamOf(DocumentWith(new Scalar(input))); - var yaml = EmittedTextFrom(events); - - var expected = string.Format("\"{0}\"", input); - - yaml.Should().Contain(expected); - } - - [Theory] - [InlineData("hello\n'world")] - public void SingleQuotesAreNotDoubleQuotedUnlessNecessary(string input) - { - var events = StreamOf(DocumentWith(new Scalar(input))); - var yaml = EmittedTextFrom(events); - yaml.Should().NotContain("\""); - } - - private string Lines(params string[] lines) - { - return string.Join(Environment.NewLine, lines); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using FluentAssertions; +using Xunit; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.RepresentationModel; + +namespace YamlDotNet.Test.Core +{ + public class EmitterTests : EmitterTestsHelper + { + [Theory] + [InlineData("01-directives.yaml")] + [InlineData("02-scalar-in-imp-doc.yaml")] + [InlineData("03-scalar-in-exp-doc.yaml")] + [InlineData("04-scalars-in-multi-docs.yaml")] + [InlineData("05-circular-sequence.yaml")] + [InlineData("06-float-tag.yaml")] + [InlineData("07-scalar-styles.yaml")] + [InlineData("08-flow-sequence.yaml")] + [InlineData("09-flow-mapping.yaml")] + [InlineData("10-mixed-nodes-in-sequence.yaml")] + [InlineData("11-mixed-nodes-in-mapping.yaml")] + [InlineData("12-compact-sequence.yaml")] + [InlineData("13-compact-mapping.yaml")] + [InlineData("14-mapping-wo-indent.yaml")] + public void CompareOriginalAndEmittedText(string filename) + { + var stream = Yaml.ReaderFrom(filename); + + var originalEvents = ParsingEventsOf(stream.ReadToEnd()); + var emittedText = EmittedTextFrom(originalEvents); + var emittedEvents = ParsingEventsOf(emittedText); + + emittedEvents.ShouldAllBeEquivalentTo(originalEvents, + opt => opt.Excluding(@event => @event.Start) + .Excluding(@event => @event.End) + .Excluding((ParsingEvent @event) => ((DocumentEnd)@event).IsImplicit)); + } + + private IList ParsingEventsOf(string text) + { + var parser = new Parser(new StringReader(text)); + return EnumerationOf(parser).ToList(); + } + + private IEnumerable EnumerationOf(IParser parser) + { + while (parser.MoveNext()) + { + yield return parser.Current; + } + } + + [Fact] + public void PlainScalarCanBeFollowedByImplicitDocument() + { + var events = StreamOf( + DocumentWith(PlainScalar("test")), + DocumentWith(PlainScalar("test"))); + + var yaml = EmittedTextFrom(events); + + yaml.Should().Contain(Lines("test", "--- test")); + } + + [Fact] + public void PlainScalarCanBeFollowedByDocumentWithVersion() + { + var events = StreamOf( + DocumentWith(PlainScalar("test")), + DocumentWithVersion(PlainScalar("test"))); + + var yaml = EmittedTextFrom(events); + + yaml.Should().Contain(Lines("test", "...", "%YAML 1.1", "--- test")); + } + + [Fact] + public void PlainScalarCanBeFollowedByDocumentWithDefaultTags() + { + var events = StreamOf( + DocumentWith(PlainScalar("test")), + DocumentWithDefaultTags(PlainScalar("test"))); + + var yaml = EmittedTextFrom(events); + + yaml.Should().Contain(Lines("test", "--- test")); + } + + [Fact] + public void PlainScalarCanBeFollowedByDocumentWithCustomTags() + { + var events = StreamOf( + DocumentWith(PlainScalar("test")), + DocumentWithCustomTags(PlainScalar("test"))); + + var yaml = EmittedTextFrom(events); + + yaml.Should().Contain(Lines("test", "...", FooTag, ExTag, ExExTag, "--- test")); + } + + [Fact] + public void BlockCanBeFollowedByImplicitDocument() + { + var events = StreamOf( + DocumentWith(SequenceWith(SingleQuotedScalar("test"))), + DocumentWith(PlainScalar("test"))); + + var yaml = EmittedTextFrom(events); + + yaml.Should().Contain(Lines("- 'test'", "--- test")); + } + + [Fact] + public void BlockCanBeFollowedByDocumentWithVersion() + { + var events = StreamOf( + DocumentWith(SequenceWith(SingleQuotedScalar("test"))), + DocumentWithVersion(PlainScalar("test"))); + + var yaml = EmittedTextFrom(events); + + yaml.Should().Contain(Lines("- 'test'", "...", "%YAML 1.1", "--- test")); + } + + [Fact] + public void BlockCanBeFollowedByDocumentWithDefaultTags() + { + var events = StreamOf( + DocumentWith(SequenceWith(SingleQuotedScalar("test"))), + DocumentWithDefaultTags(PlainScalar("test"))); + + var yaml = EmittedTextFrom(events); + + yaml.Should().Contain(Lines("- 'test'", "--- test")); + } + + [Fact] + public void BlockCanBeFollowedByDocumentWithCustomTags() + { + var events = StreamOf( + DocumentWith(SequenceWith(SingleQuotedScalar("test"))), + DocumentWithCustomTags(PlainScalar("test"))); + + var yaml = EmittedTextFrom(events); + + yaml.Should().Contain(Lines("- 'test'", "...", FooTag, ExTag, ExExTag, "--- test")); + } + + [Theory] + [InlineData("test", ">-\r\n test\r\n")] // No indentation indicator when no indent. + [InlineData(" test", ">2-\r\n test\r\n")] + public void BlockStyleGeneratesIndentationIndicator(string input, string expected) + { + var events = StreamOf( + DocumentWith(FoldedScalar(input))); + + var yaml = EmittedTextFrom(events); + + yaml.Should().Be(expected.NormalizeNewLines()); + } + + [Theory] + [InlineData("LF hello\nworld")] + [InlineData("CRLF hello\r\nworld")] + public void FoldedStyleDoesNotLooseCharacters(string text) + { + var events = SequenceWith(FoldedScalar(text)); + + var yaml = EmittedTextFrom(StreamedDocumentWith(events)); + + yaml.Should().Contain("world"); + } + + [Fact] + public void FoldedStyleIsSelectedWhenNewLinesAreFoundInLiteral() + { + var events = SequenceWith(Scalar("hello\nworld").ImplicitPlain); + + var yaml = EmittedTextFrom(StreamedDocumentWith(events)); + + yaml.Should().Contain(">"); + } + + + [Fact] + [Trait("motive", "pr #540")] + public void AllowBlockStyleInMultilineScalarsWithTrailingSpaces() + { + var events = SequenceWith(Scalar("hello \nworld").ImplicitPlain); + + var yaml = EmittedTextFrom(StreamedDocumentWith(events)); + + yaml.Should().Contain("\n"); + } + + + [Fact] + public void FoldedStyleDoesNotGenerateExtraLineBreaks() + { + var events = SequenceWith(FoldedScalar("hello\nworld")); + + var yaml = EmittedTextFrom(StreamedDocumentWith(events)); + + // Todo: Why involve the rep. model when testing the Emitter? Can we match using a regex? + var stream = new YamlStream(); + stream.Load(new StringReader(yaml)); + var sequence = (YamlSequenceNode)stream.Documents[0].RootNode; + var scalar = (YamlScalarNode)sequence.Children[0]; + + scalar.Value.Should().Be("hello\nworld"); + } + + [Fact] + public void FoldedStyleDoesNotCollapseLineBreaks() + { + var events = SequenceWith(FoldedScalar(">+\n")); + + var yaml = EmittedTextFrom(StreamedDocumentWith(events)); + + var stream = new YamlStream(); + stream.Load(new StringReader(yaml)); + var sequence = (YamlSequenceNode)stream.Documents[0].RootNode; + var scalar = (YamlScalarNode)sequence.Children[0]; + + scalar.Value.Should().Be(">+\n"); + } + + [Fact] + [Trait("motive", "issue #39")] + public void FoldedStylePreservesNewLines() + { + var input = "id: 0\nPayload:\n X: 5\n Y: 6\n"; + var events = MappingWith( + Scalar("Payload").ImplicitPlain, + FoldedScalar(input)); + + var yaml = EmittedTextFrom(StreamedDocumentWith(events)); + + var stream = new YamlStream(); + stream.Load(new StringReader(yaml)); + + var mapping = (YamlMappingNode)stream.Documents[0].RootNode; + var value = (YamlScalarNode)mapping.Children.First().Value; + + value.Value.Should().Be(input); + } + + [Fact] + public void CommentsAreEmittedCorrectly() + { + var events = SequenceWith( + StandaloneComment("Top comment"), + StandaloneComment("Second line"), + Scalar("first").ImplicitPlain, + InlineComment("The first value"), + Scalar("second").ImplicitPlain, + InlineComment("The second value"), + StandaloneComment("Bottom comment") + ); + + var yaml = EmittedTextFrom(StreamedDocumentWith(events)); + + yaml.Should() + .Contain("# Top comment") + .And.Contain("# Second line") + .And.NotContain("# Top comment # Second line") + .And.Contain("first # The first value") + .And.Contain("second # The second value") + .And.Contain("# Bottom comment"); + } + + [Fact] + public void ACommentAsTheFirstEventAddsANewLine() + { + var events = new ParsingEvent[] + { + StandaloneComment("Top comment"), + Scalar("first").ImplicitPlain, + }; + + var yaml = EmittedTextFrom(StreamedDocumentWith(events)); + + yaml.Should() + .Contain("# Top comment") + .And.Contain("first") + .And.NotContain("# Top commentfirst"); + } + + [Theory] + [InlineData("Гранит", 28595)] // Cyrillic (ISO) + [InlineData("ГÀƊȽ˱ώҔׂۋᵷẁό₩וּﺪﺸﻸﭧ╬♫₹Ὰỗ᷁ݭ٭ӢР͞ʓLjĄë0", 65001)] // UTF-8 + public void UnicodeInScalarsCanBeSingleQuotedWhenOutputEncodingSupportsIt(string text, int codePage) + { + var document = StreamedDocumentWith( + SequenceWith( + SingleQuotedScalar(text) + ) + ); + var buffer = new MemoryStream(); +#if (NETCOREAPP2_1_OR_GREATER) + // Code pages such as Cyrillic are not recognized by default in + // .NET Core. We need to register this provider. + // https://msdn.microsoft.com/en-us/library/mt643899(v=vs.110).aspx#Remarks + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); +#endif + var encoding = Encoding.GetEncoding(codePage); + using (var writer = new StreamWriter(buffer, encoding)) + { + var emitter = new Emitter(writer, 2, int.MaxValue, false); + foreach (var evt in document) + { + emitter.Emit(evt); + } + } + + var yaml = encoding.GetString(buffer.ToArray()); + + yaml.Should() + .Contain("'" + text + "'"); + } + + [Fact] + public void EmptyStringsAreQuoted() + { + var events = SequenceWith( + Scalar(string.Empty).ImplicitPlain + ); + + var yaml = EmittedTextFrom(StreamedDocumentWith(events)); + yaml.Should() + .Contain("- ''"); + } + + [Theory] + [InlineData("b-carriage-return,b-line-feed\r\nlll", "b-carriage-return,b-line-feed\nlll")] + [InlineData("b-carriage-return,b-line-feed\r\n\r\nlll", "b-carriage-return,b-line-feed\n\nlll")] + [InlineData("b-carriage-return\rlll", "b-carriage-return\nlll")] + [InlineData("b-line-feed\nlll", "b-line-feed\nlll")] + [InlineData("b-next-line\x85lll", "b-next-line\nlll")] + [InlineData("b-line-separator\x2028lll", "b-line-separator\x2028lll")] + [InlineData("b-paragraph-separator\x2029lll", "b-paragraph-separator\x2029lll")] + public void NewLinesAreNotDuplicatedWhenEmitted(string input, string expected) + { + var yaml = EmittedTextFrom(StreamOf(DocumentWith( + LiteralScalar(input) + ))); + + AssertSequenceOfEventsFrom(Yaml.ParserForText(yaml), + StreamStart, + DocumentStart(Implicit), + LiteralScalar(expected), + DocumentEnd(Implicit), + StreamEnd); + } + + [Theory] + [InlineData("b-carriage-return,b-line-feed\r\nlll", "b-carriage-return,b-line-feed\nlll")] + [InlineData("b-carriage-return,b-line-feed\r\n\r\nlll", "b-carriage-return,b-line-feed\n\nlll")] + [InlineData("b-carriage-return\rlll", "b-carriage-return\nlll")] + [InlineData("b-line-feed\nlll", "b-line-feed\nlll")] + [InlineData("b-next-line\x85lll", "b-next-line\nlll")] + [InlineData("b-line-separator\x2028lll", "b-line-separator\x2028lll")] + [InlineData("b-paragraph-separator\x2029lll", "b-paragraph-separator\x2029lll")] + public void NewLinesAreNotDuplicatedWhenEmittedInFoldedScalar(string input, string expected) + { + var yaml = EmittedTextFrom(StreamOf(DocumentWith( + FoldedScalar(input) + ))); + + AssertSequenceOfEventsFrom(Yaml.ParserForText(yaml), + StreamStart, + DocumentStart(Implicit), + FoldedScalar(expected), + DocumentEnd(Implicit), + StreamEnd); + } + + [Theory] + [InlineData("'.'test")] + [InlineData("'")] + [InlineData("'.'")] + [InlineData("'test")] + [InlineData("'test'")] + public void SingleQuotesAreDoubleQuoted(string input) + { + var events = StreamOf(DocumentWith(new Scalar(input))); + var yaml = EmittedTextFrom(events); + + var expected = string.Format("\"{0}\"", input); + + yaml.Should().Contain(expected); + } + + [Theory] + [InlineData("hello\n'world")] + public void SingleQuotesAreNotDoubleQuotedUnlessNecessary(string input) + { + var events = StreamOf(DocumentWith(new Scalar(input))); + var yaml = EmittedTextFrom(events); + yaml.Should().NotContain("\""); + } + + private string Lines(params string[] lines) + { + return string.Join(Environment.NewLine, lines); + } + } +} diff --git a/YamlDotNet.Test/Core/EmitterTestsHelper.cs b/YamlDotNet.Test/Core/EmitterTestsHelper.cs index 28bb35faf..47ff45d9a 100644 --- a/YamlDotNet.Test/Core/EmitterTestsHelper.cs +++ b/YamlDotNet.Test/Core/EmitterTestsHelper.cs @@ -1,116 +1,116 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Core.Tokens; - -namespace YamlDotNet.Test.Core -{ - public class EmitterTestsHelper : EventsHelper - { - protected const string FooTag = "%TAG !foo! tag:example.org:foo"; - protected const string ExTag = "%TAG ! !"; - protected const string ExExTag = "%TAG !! tag:yaml.org,2002:"; - - protected string EmittedTextFrom(IEnumerable events) - { - return Emit(events, EmitterWithIndentCreator); - } - - private Func EmitterWithIndentCreator - { - get { return writer => new Emitter(writer, 2, int.MaxValue, false); } - } - - protected string Emit(IEnumerable events, Func createEmitter) - { - var writer = new StringWriter(); - var emitter = createEmitter(writer); - events.Run(emitter.Emit); - return writer.ToString(); - } - - protected IEnumerable StreamedDocumentWith(IEnumerable events) - { - return StreamOf(DocumentWith(events.ToArray())); - } - - protected IEnumerable StreamOf(params IEnumerable[] documents) - { - var allEvents = documents.SelectMany(x => x); - return Wrap(allEvents, StreamStart, StreamEnd); - } - - protected IEnumerable DocumentWithVersion(params ParsingEvent[] events) - { - var version = new VersionDirective(new YamlDotNet.Core.Version(1, 1)); - return Wrap(events, DocumentStart(Explicit, version), DocumentEnd(Implicit)); - } - - protected IEnumerable DocumentWithDefaultTags(params ParsingEvent[] events) - { - var tags = Constants.DefaultTagDirectives; - return Wrap(events, DocumentStart(Explicit, null, tags), DocumentEnd(Implicit)); - } - - protected IEnumerable DocumentWithCustomTags(params ParsingEvent[] events) - { - var parts = FooTag.Split(' '); - var tags = new TagDirective(parts[1], parts[2]); - return Wrap(events, DocumentStart(Explicit, null, tags), DocumentEnd(Implicit)); - } - - protected IEnumerable DocumentWith(IEnumerable events) - { - return DocumentWith(events.ToArray()); - } - - protected IEnumerable DocumentWith(params ParsingEvent[] events) - { - return Wrap(events, DocumentStart(Implicit), DocumentEnd(Implicit)); - } - - protected IEnumerable SequenceWith(params ParsingEvent[] events) - { - return Wrap(events, BlockSequenceStart.Explicit, SequenceEnd); - } - - protected IEnumerable MappingWith(params ParsingEvent[] events) - { - return Wrap(events, MappingStart, MappingEnd); - } - - private IEnumerable Wrap(IEnumerable events, ParsingEvent start, ParsingEvent end) - { - yield return start; - foreach (var @event in events) - { - yield return @event; - } - yield return end; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Core.Tokens; + +namespace YamlDotNet.Test.Core +{ + public class EmitterTestsHelper : EventsHelper + { + protected const string FooTag = "%TAG !foo! tag:example.org:foo"; + protected const string ExTag = "%TAG ! !"; + protected const string ExExTag = "%TAG !! tag:yaml.org,2002:"; + + protected string EmittedTextFrom(IEnumerable events) + { + return Emit(events, EmitterWithIndentCreator); + } + + private Func EmitterWithIndentCreator + { + get { return writer => new Emitter(writer, 2, int.MaxValue, false); } + } + + protected string Emit(IEnumerable events, Func createEmitter) + { + var writer = new StringWriter(); + var emitter = createEmitter(writer); + events.Run(emitter.Emit); + return writer.ToString(); + } + + protected IEnumerable StreamedDocumentWith(IEnumerable events) + { + return StreamOf(DocumentWith(events.ToArray())); + } + + protected IEnumerable StreamOf(params IEnumerable[] documents) + { + var allEvents = documents.SelectMany(x => x); + return Wrap(allEvents, StreamStart, StreamEnd); + } + + protected IEnumerable DocumentWithVersion(params ParsingEvent[] events) + { + var version = new VersionDirective(new YamlDotNet.Core.Version(1, 1)); + return Wrap(events, DocumentStart(Explicit, version), DocumentEnd(Implicit)); + } + + protected IEnumerable DocumentWithDefaultTags(params ParsingEvent[] events) + { + var tags = Constants.DefaultTagDirectives; + return Wrap(events, DocumentStart(Explicit, null, tags), DocumentEnd(Implicit)); + } + + protected IEnumerable DocumentWithCustomTags(params ParsingEvent[] events) + { + var parts = FooTag.Split(' '); + var tags = new TagDirective(parts[1], parts[2]); + return Wrap(events, DocumentStart(Explicit, null, tags), DocumentEnd(Implicit)); + } + + protected IEnumerable DocumentWith(IEnumerable events) + { + return DocumentWith(events.ToArray()); + } + + protected IEnumerable DocumentWith(params ParsingEvent[] events) + { + return Wrap(events, DocumentStart(Implicit), DocumentEnd(Implicit)); + } + + protected IEnumerable SequenceWith(params ParsingEvent[] events) + { + return Wrap(events, BlockSequenceStart.Explicit, SequenceEnd); + } + + protected IEnumerable MappingWith(params ParsingEvent[] events) + { + return Wrap(events, MappingStart, MappingEnd); + } + + private IEnumerable Wrap(IEnumerable events, ParsingEvent start, ParsingEvent end) + { + yield return start; + foreach (var @event in events) + { + yield return @event; + } + yield return end; + } + } +} diff --git a/YamlDotNet.Test/Core/EventsHelper.cs b/YamlDotNet.Test/Core/EventsHelper.cs index a91e80ec2..8bd1ea833 100644 --- a/YamlDotNet.Test/Core/EventsHelper.cs +++ b/YamlDotNet.Test/Core/EventsHelper.cs @@ -1,336 +1,336 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections; -using System.Reflection; -using FluentAssertions; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using TagDirective = YamlDotNet.Core.Tokens.TagDirective; -using VersionDirective = YamlDotNet.Core.Tokens.VersionDirective; - -// ReSharper disable MemberHidesStaticFromOuterClass -namespace YamlDotNet.Test.Core -{ - public class EventsHelper - { - protected const bool Explicit = false; - protected const bool Implicit = true; - protected const string TagYaml = "tag:yaml.org,2002:"; - - protected static readonly TagDirective[] DefaultTags = - { - new TagDirective("!", "!"), - new TagDirective("!!", TagYaml) - }; - - protected static StreamStart StreamStart - { - get { return new StreamStart(); } - } - - protected static StreamEnd StreamEnd - { - get { return new StreamEnd(); } - } - - protected DocumentStart DocumentStart(bool isImplicit) - { - return DocumentStart(isImplicit, null, DefaultTags); - } - - protected DocumentStart DocumentStart(bool isImplicit, VersionDirective version, params TagDirective[] tags) - { - return new DocumentStart(version, new TagDirectiveCollection(tags), isImplicit); - } - - protected VersionDirective Version(int major, int minor) - { - return new VersionDirective(new Version(major, minor)); - } - - protected TagDirective TagDirective(string handle, string prefix) - { - return new TagDirective(handle, prefix); - } - - protected DocumentEnd DocumentEnd(bool isImplicit) - { - return new DocumentEnd(isImplicit); - } - - protected ScalarBuilder Scalar(string text) - { - return new ScalarBuilder(text, ScalarStyle.Any); - } - - protected ScalarBuilder PlainScalar(string text) - { - return new ScalarBuilder(text, ScalarStyle.Plain); - } - - protected ScalarBuilder SingleQuotedScalar(string text) - { - return new ScalarBuilder(text, ScalarStyle.SingleQuoted); - } - - protected ScalarBuilder DoubleQuotedScalar(string text) - { - return new ScalarBuilder(text, ScalarStyle.DoubleQuoted); - } - - protected ScalarBuilder LiteralScalar(string text) - { - return new ScalarBuilder(text, ScalarStyle.Literal); - } - - protected ScalarBuilder FoldedScalar(string text) - { - return new ScalarBuilder(text, ScalarStyle.Folded); - } - - protected SequenceStartBuilder BlockSequenceStart - { - get { return new SequenceStartBuilder(SequenceStyle.Block); } - } - - protected SequenceStartBuilder FlowSequenceStart - { - get { return new SequenceStartBuilder(SequenceStyle.Flow); } - } - - protected SequenceEnd SequenceEnd - { - get { return new SequenceEnd(); } - } - - protected MappingStart MappingStart - { - get { return new MappingStart(); } - } - - protected MappingStartBuilder BlockMappingStart - { - get { return new MappingStartBuilder(MappingStyle.Block); } - } - - protected MappingStartBuilder FlowMappingStart - { - get { return new MappingStartBuilder(MappingStyle.Flow); } - } - - protected MappingEnd MappingEnd - { - get { return new MappingEnd(); } - } - - protected AnchorAlias AnchorAlias(string alias) - { - return new AnchorAlias(new AnchorName(alias)); - } - - protected Comment StandaloneComment(string value) - { - return new Comment(value, false); - } - - protected Comment InlineComment(string value) - { - return new Comment(value, true); - } - - protected class ScalarBuilder - { - private readonly string text; - private readonly ScalarStyle style; - private string tag; - private bool plainImplicit; - private bool quotedImplicit; - - public ScalarBuilder(string text, ScalarStyle style) - { - this.text = text; - this.style = style; - plainImplicit = style == ScalarStyle.Plain; - quotedImplicit = style != ScalarStyle.Plain && - style != ScalarStyle.Any; - } - - public ScalarBuilder T(string tag) - { - this.tag = tag; - plainImplicit = false; - quotedImplicit = false; - return this; - } - - public ScalarBuilder ImplicitPlain - { - get - { - plainImplicit = true; - return this; - } - } - - public ScalarBuilder ImplicitQuoted - { - get - { - quotedImplicit = true; - return this; - } - } - - public static implicit operator Scalar(ScalarBuilder builder) - { - return new Scalar(AnchorName.Empty, - builder.tag, - builder.text, - builder.style, - builder.plainImplicit, - builder.quotedImplicit); - } - } - - protected class SequenceStartBuilder - { - private const bool DefaultImplicit = true; - - private readonly SequenceStyle style; - private AnchorName anchor; - private bool @implicit; - - public SequenceStartBuilder(SequenceStyle style) - { - this.style = style; - @implicit = DefaultImplicit; - } - - public SequenceStartBuilder A(string anchor) - { - this.anchor = new AnchorName(anchor); - return this; - } - - public SequenceStartBuilder Explicit - { - get - { - @implicit = false; - return this; - } - } - - public static implicit operator SequenceStart(SequenceStartBuilder builder) - { - return new SequenceStart(builder.anchor, null, builder.@implicit, builder.style); - } - } - - protected class MappingStartBuilder - { - private const bool DefaultImplicit = true; - - private readonly MappingStyle style; - private string tag; - private bool @implicit; - - public MappingStartBuilder(MappingStyle style) - { - this.style = style; - @implicit = DefaultImplicit; - } - - public MappingStartBuilder T(string tag) - { - this.tag = tag; - return this; - } - - public MappingStartBuilder Explicit - { - get - { - @implicit = false; - return this; - } - } - - public static implicit operator MappingStart(MappingStartBuilder builder) - { - return new MappingStart(AnchorName.Empty, builder.tag, builder.@implicit, builder.style); - } - } - - protected void AssertSequenceOfEventsFrom(IParser parser, params ParsingEvent[] events) - { - var eventNumber = 1; - foreach (var expected in events) - { - parser.MoveNext().Should().BeTrue("Missing parse event number {0}", eventNumber); - AssertEvent(expected, parser.Current, eventNumber); - eventNumber++; - } - parser.MoveNext().Should().BeFalse("Found extra parse events"); - } - - protected void AssertEvent(ParsingEvent expected, ParsingEvent actual, int eventNumber) - { - actual.GetType().Should().Be(expected.GetType(), "Parse event {0} is not of the expected type.", eventNumber); - - foreach (var property in expected.GetType().GetTypeInfo().GetProperties()) - { - if (property.PropertyType == typeof(Mark) || !property.CanRead || property.Name == "IsKey") - { - continue; - } - - var value = property.GetValue(actual, null); - var expectedValue = property.GetValue(expected, null); - if (expectedValue is IEnumerable enumerable && !(expectedValue is string)) - { - if (expectedValue is ICollection expectedCollection && value is ICollection valueCollection) - { - var expectedCount = expectedCollection.Count; - var valueCount = valueCollection.Count; - valueCount.Should().Be(expectedCount, "Compared size of collections in property {0} in parse event {1}", - property.Name, eventNumber); - } - - var values = ((IEnumerable)value).GetEnumerator(); - var expectedValues = enumerable.GetEnumerator(); - while (expectedValues.MoveNext()) - { - values.MoveNext().Should().BeTrue("Property {0} in parse event {1} had too few elements", property.Name, eventNumber); - values.Current.Should().Be(expectedValues.Current, - "Compared element in property {0} in parse event {1}", property.Name, eventNumber); - } - values.MoveNext().Should().BeFalse("Property {0} in parse event {1} had too many elements", property.Name, eventNumber); - } - else - { - value.Should().Be(expectedValue, "Compared property {0} in parse event {1}", property.Name, eventNumber); - } - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections; +using System.Reflection; +using FluentAssertions; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using TagDirective = YamlDotNet.Core.Tokens.TagDirective; +using VersionDirective = YamlDotNet.Core.Tokens.VersionDirective; + +// ReSharper disable MemberHidesStaticFromOuterClass +namespace YamlDotNet.Test.Core +{ + public class EventsHelper + { + protected const bool Explicit = false; + protected const bool Implicit = true; + protected const string TagYaml = "tag:yaml.org,2002:"; + + protected static readonly TagDirective[] DefaultTags = + { + new TagDirective("!", "!"), + new TagDirective("!!", TagYaml) + }; + + protected static StreamStart StreamStart + { + get { return new StreamStart(); } + } + + protected static StreamEnd StreamEnd + { + get { return new StreamEnd(); } + } + + protected DocumentStart DocumentStart(bool isImplicit) + { + return DocumentStart(isImplicit, null, DefaultTags); + } + + protected DocumentStart DocumentStart(bool isImplicit, VersionDirective version, params TagDirective[] tags) + { + return new DocumentStart(version, new TagDirectiveCollection(tags), isImplicit); + } + + protected VersionDirective Version(int major, int minor) + { + return new VersionDirective(new Version(major, minor)); + } + + protected TagDirective TagDirective(string handle, string prefix) + { + return new TagDirective(handle, prefix); + } + + protected DocumentEnd DocumentEnd(bool isImplicit) + { + return new DocumentEnd(isImplicit); + } + + protected ScalarBuilder Scalar(string text) + { + return new ScalarBuilder(text, ScalarStyle.Any); + } + + protected ScalarBuilder PlainScalar(string text) + { + return new ScalarBuilder(text, ScalarStyle.Plain); + } + + protected ScalarBuilder SingleQuotedScalar(string text) + { + return new ScalarBuilder(text, ScalarStyle.SingleQuoted); + } + + protected ScalarBuilder DoubleQuotedScalar(string text) + { + return new ScalarBuilder(text, ScalarStyle.DoubleQuoted); + } + + protected ScalarBuilder LiteralScalar(string text) + { + return new ScalarBuilder(text, ScalarStyle.Literal); + } + + protected ScalarBuilder FoldedScalar(string text) + { + return new ScalarBuilder(text, ScalarStyle.Folded); + } + + protected SequenceStartBuilder BlockSequenceStart + { + get { return new SequenceStartBuilder(SequenceStyle.Block); } + } + + protected SequenceStartBuilder FlowSequenceStart + { + get { return new SequenceStartBuilder(SequenceStyle.Flow); } + } + + protected SequenceEnd SequenceEnd + { + get { return new SequenceEnd(); } + } + + protected MappingStart MappingStart + { + get { return new MappingStart(); } + } + + protected MappingStartBuilder BlockMappingStart + { + get { return new MappingStartBuilder(MappingStyle.Block); } + } + + protected MappingStartBuilder FlowMappingStart + { + get { return new MappingStartBuilder(MappingStyle.Flow); } + } + + protected MappingEnd MappingEnd + { + get { return new MappingEnd(); } + } + + protected AnchorAlias AnchorAlias(string alias) + { + return new AnchorAlias(new AnchorName(alias)); + } + + protected Comment StandaloneComment(string value) + { + return new Comment(value, false); + } + + protected Comment InlineComment(string value) + { + return new Comment(value, true); + } + + protected class ScalarBuilder + { + private readonly string text; + private readonly ScalarStyle style; + private string tag; + private bool plainImplicit; + private bool quotedImplicit; + + public ScalarBuilder(string text, ScalarStyle style) + { + this.text = text; + this.style = style; + plainImplicit = style == ScalarStyle.Plain; + quotedImplicit = style != ScalarStyle.Plain && + style != ScalarStyle.Any; + } + + public ScalarBuilder T(string tag) + { + this.tag = tag; + plainImplicit = false; + quotedImplicit = false; + return this; + } + + public ScalarBuilder ImplicitPlain + { + get + { + plainImplicit = true; + return this; + } + } + + public ScalarBuilder ImplicitQuoted + { + get + { + quotedImplicit = true; + return this; + } + } + + public static implicit operator Scalar(ScalarBuilder builder) + { + return new Scalar(AnchorName.Empty, + builder.tag, + builder.text, + builder.style, + builder.plainImplicit, + builder.quotedImplicit); + } + } + + protected class SequenceStartBuilder + { + private const bool DefaultImplicit = true; + + private readonly SequenceStyle style; + private AnchorName anchor; + private bool @implicit; + + public SequenceStartBuilder(SequenceStyle style) + { + this.style = style; + @implicit = DefaultImplicit; + } + + public SequenceStartBuilder A(string anchor) + { + this.anchor = new AnchorName(anchor); + return this; + } + + public SequenceStartBuilder Explicit + { + get + { + @implicit = false; + return this; + } + } + + public static implicit operator SequenceStart(SequenceStartBuilder builder) + { + return new SequenceStart(builder.anchor, null, builder.@implicit, builder.style); + } + } + + protected class MappingStartBuilder + { + private const bool DefaultImplicit = true; + + private readonly MappingStyle style; + private string tag; + private bool @implicit; + + public MappingStartBuilder(MappingStyle style) + { + this.style = style; + @implicit = DefaultImplicit; + } + + public MappingStartBuilder T(string tag) + { + this.tag = tag; + return this; + } + + public MappingStartBuilder Explicit + { + get + { + @implicit = false; + return this; + } + } + + public static implicit operator MappingStart(MappingStartBuilder builder) + { + return new MappingStart(AnchorName.Empty, builder.tag, builder.@implicit, builder.style); + } + } + + protected void AssertSequenceOfEventsFrom(IParser parser, params ParsingEvent[] events) + { + var eventNumber = 1; + foreach (var expected in events) + { + parser.MoveNext().Should().BeTrue("Missing parse event number {0}", eventNumber); + AssertEvent(expected, parser.Current, eventNumber); + eventNumber++; + } + parser.MoveNext().Should().BeFalse("Found extra parse events"); + } + + protected void AssertEvent(ParsingEvent expected, ParsingEvent actual, int eventNumber) + { + actual.GetType().Should().Be(expected.GetType(), "Parse event {0} is not of the expected type.", eventNumber); + + foreach (var property in expected.GetType().GetTypeInfo().GetProperties()) + { + if (property.PropertyType == typeof(Mark) || !property.CanRead || property.Name == "IsKey") + { + continue; + } + + var value = property.GetValue(actual, null); + var expectedValue = property.GetValue(expected, null); + if (expectedValue is IEnumerable enumerable && !(expectedValue is string)) + { + if (expectedValue is ICollection expectedCollection && value is ICollection valueCollection) + { + var expectedCount = expectedCollection.Count; + var valueCount = valueCollection.Count; + valueCount.Should().Be(expectedCount, "Compared size of collections in property {0} in parse event {1}", + property.Name, eventNumber); + } + + var values = ((IEnumerable)value).GetEnumerator(); + var expectedValues = enumerable.GetEnumerator(); + while (expectedValues.MoveNext()) + { + values.MoveNext().Should().BeTrue("Property {0} in parse event {1} had too few elements", property.Name, eventNumber); + values.Current.Should().Be(expectedValues.Current, + "Compared element in property {0} in parse event {1}", property.Name, eventNumber); + } + values.MoveNext().Should().BeFalse("Property {0} in parse event {1} had too many elements", property.Name, eventNumber); + } + else + { + value.Should().Be(expectedValue, "Compared property {0} in parse event {1}", property.Name, eventNumber); + } + } + } + } +} diff --git a/YamlDotNet.Test/Core/InsertionQueueTests.cs b/YamlDotNet.Test/Core/InsertionQueueTests.cs index d70a19c3e..6cc49c5a2 100644 --- a/YamlDotNet.Test/Core/InsertionQueueTests.cs +++ b/YamlDotNet.Test/Core/InsertionQueueTests.cs @@ -1,358 +1,358 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using FluentAssertions; -using Xunit; -using Xunit.Abstractions; -using YamlDotNet.Core; - -namespace YamlDotNet.Test.Core -{ - public class InsertionQueueTests - { - private readonly ITestOutputHelper output; - - public InsertionQueueTests(ITestOutputHelper output) - { - this.output = output; - } - - [Theory] - [InlineData("-43210--", 0, "-43210X-")] - [InlineData("---43210", 0, "X--43210")] - [InlineData("10---432", 0, "10X--432")] - - [InlineData("--43210-", 5, "-X43210-")] - [InlineData("43210---", 5, "43210--X")] - [InlineData("210---43", 5, "210--X43")] - - [InlineData("-43210--", 2, "-432X10-")] - [InlineData("---43210", 2, "--432X10")] - [InlineData("43210---", 2, "432X10--")] - [InlineData("210---43", 2, "2X10--43")] - [InlineData("10---432", 2, "10--432X")] - - [InlineData("-43210--", 4, "4X3210--")] - public void CalculateInsertionParameters_is_correct(string initialState, int index, string expectedFinalState) - { - static (int capacity, int readPtr, int writePtr, int insertPtr, List<(char chr, int idx)> elements) ParseState(string state) - { - var chars = state.ToArray() - .Select((chr, idx) => (chr, idx)) - .ToList(); - - var elements = chars - .Where(e => e.chr != '-') - .ToList(); - - var insertPtr = chars - .Where(e => e.chr == 'X') - .Select(e => e.idx) - .FirstOrDefault(); - - var readPtr = chars - .SkipWhile(e => e.chr == '-') - .TakeWhile(e => e.chr != '-') - .Last() - .idx; - - var writePtr = chars - .SkipWhile(e => e.chr != '-') - .TakeWhile(e => e.chr == '-') - .Last() - .idx; - - return (state.Length, readPtr, writePtr, insertPtr, elements); - } - - var (capacity, readPtr, writePtr, _, initialElements) = ParseState(initialState); - var (finalCapacity, expectedReadPtr, expectedWritePtr, expectedInsertPtr, finalElements) = ParseState(expectedFinalState); - - if (capacity != finalCapacity) - { - throw new InvalidOperationException($"Invalid test data: capacity: {capacity}, finalCapacity: {finalCapacity}"); - } - - var capacityIsPowerOf2 = - (int)(Math.Ceiling((Math.Log(capacity) / Math.Log(2)))) == - (int)(Math.Floor(((Math.Log(capacity) / Math.Log(2))))); - - if (!capacityIsPowerOf2) - { - throw new InvalidOperationException($"Capacity should be a power of 2, but was {capacity}."); - } - - output.WriteLine("Initial State"); - output.WriteLine("============="); - output.WriteLine(""); - PrintChars((readPtr, 'R'), (writePtr, 'W')); - output.WriteLine(initialState); - output.WriteLine(""); - - output.WriteLine("Expected Final State"); - output.WriteLine("===================="); - output.WriteLine(""); - PrintChars((expectedReadPtr, 'R'), (expectedWritePtr, 'W')); - output.WriteLine(expectedFinalState); - PrintChars((expectedInsertPtr, '^')); - output.WriteLine(""); - - var movedElements = initialElements - .Join(finalElements, e => e.chr, e => e.chr, (i, f) => (i.chr, from: i.idx, offset: f.idx - i.idx)) - .Where(c => c.offset != 0) - .ToList(); - - int expectedCopyIndex, expectedCopyOffset, expectedCopyLength; - if (movedElements.Count != 0) - { - if (movedElements.Select(e => e.offset).Distinct().Count() != 1) - { - throw new InvalidOperationException("Invalid test data"); - } - - expectedCopyIndex = movedElements.Select(e => e.from).Min(); - expectedCopyOffset = movedElements[0].offset; - expectedCopyLength = movedElements.Count; - } - else - { - expectedCopyIndex = 0; - expectedCopyOffset = 0; - expectedCopyLength = 0; - } - - var mask = capacity - 1; // Assuming that capacity is a power of 2 - - InsertionQueue.CalculateInsertionParameters(mask, initialElements.Count, index, ref readPtr, ref writePtr, out var insertPtr, out var copyIndex, out var copyOffset, out var copyLength); - - static string Format(int readPtr, int writePtr, int insertPtr, int copyIndex, int copyOffset, int copyLength) - { - return $"readPtr: {readPtr}, writePtr: {writePtr}, insertPtr: {insertPtr}, copyIndex: {copyIndex}, copyOffset: {copyOffset}, copyLength: {copyLength}"; - } - - var expected = Format(expectedReadPtr, expectedWritePtr, expectedInsertPtr, expectedCopyIndex, expectedCopyOffset, expectedCopyLength); - var actual = Format(readPtr, writePtr, insertPtr, copyIndex, copyOffset, copyLength); - - output.WriteLine($"expected: {expected}"); - output.WriteLine($"actual: {actual}"); - - Assert.Equal(expected, actual); - } - - [Theory] - [InlineData(0, 4)] - [InlineData(1, 4)] - [InlineData(2, 4)] - [InlineData(3, 4)] - public void Resize_is_is_correct_when_enqueuing(int offsetBeforeResize, int initialCapacity) - { - var sut = new InsertionQueue(initialCapacity); - - for (var i = 0; i < offsetBeforeResize; ++i) - { - sut.Enqueue(-1); - sut.Dequeue(); - } - - for (var i = 0; i < initialCapacity; ++i) - { - sut.Enqueue(i + 1); - } - - // Sanity checks - Assert.Equal(initialCapacity, sut.Capacity); - Assert.Equal(Enumerable.Range(1, initialCapacity), sut); - - sut.Enqueue(initialCapacity + 1); - - Assert.Equal(initialCapacity * 2, sut.Capacity); - Assert.Equal(Enumerable.Range(1, initialCapacity + 1), sut); - } - - [Theory] - [InlineData(0, 0, 4)] - [InlineData(0, 1, 4)] - [InlineData(0, 2, 4)] - [InlineData(0, 3, 4)] - [InlineData(0, 4, 4)] - [InlineData(1, 0, 4)] - [InlineData(1, 1, 4)] - [InlineData(1, 2, 4)] - [InlineData(1, 3, 4)] - [InlineData(1, 4, 4)] - [InlineData(2, 0, 4)] - [InlineData(2, 1, 4)] - [InlineData(2, 2, 4)] - [InlineData(2, 3, 4)] - [InlineData(2, 4, 4)] - [InlineData(3, 0, 4)] - [InlineData(3, 1, 4)] - [InlineData(3, 2, 4)] - [InlineData(3, 3, 4)] - [InlineData(3, 4, 4)] - public void Resize_is_is_correct_when_inserting(int offsetBeforeResize, int insertionIndex, int initialCapacity) - { - var sut = new InsertionQueue(initialCapacity); - - for (var i = 0; i < offsetBeforeResize; ++i) - { - sut.Enqueue(-1); - sut.Dequeue(); - } - - for (var i = 0; i < initialCapacity; ++i) - { - sut.Enqueue(i + 1); - } - - // Sanity checks - Assert.Equal(initialCapacity, sut.Capacity); - Assert.Equal(Enumerable.Range(1, initialCapacity), sut); - - sut.Insert(insertionIndex, initialCapacity + 1); - - Assert.Equal(initialCapacity * 2, sut.Capacity); - - var expectedSequence = - Enumerable.Range(1, insertionIndex) - .Concat(new[] { initialCapacity + 1 }) - .Concat(Enumerable.Range(insertionIndex + 1, initialCapacity - insertionIndex)); - - Assert.Equal(expectedSequence, sut); - - sut.Enqueue(-1); - } - - private void PrintChars(params (int idx, char chr)[] characters) - { - var text = new char[characters.Max(c => c.idx) + 1]; - for (var i = 0; i < text.Length; ++i) - { - text[i] = ' '; - } - - foreach (var (idx, chr) in characters) - { - text[idx] = chr; - } - - output.WriteLine(new string(text)); - } - - [Fact] - public void ShouldThrowExceptionWhenDequeuingEmptyContainer() - { - var queue = CreateQueue(); - - Action action = () => queue.Dequeue(); - - action.ShouldThrow(); - } - - [Fact] - public void ShouldThrowExceptionWhenDequeuingContainerThatBecomesEmpty() - { - var queue = CreateQueue(); - - queue.Enqueue(1); - queue.Dequeue(); - Action action = () => queue.Dequeue(); - - action.ShouldThrow(); - } - - [Fact] - public void ShouldCorrectlyDequeueElementsAfterEnqueuing() - { - var queue = CreateQueue(); - - WithTheRange(0, 10).Run(queue.Enqueue); - - OrderOfElementsIn(queue).Should().Equal(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - } - - [Fact] - public void ShouldCorrectlyDequeueElementsWhenIntermixingEnqueuing() - { - var queue = CreateQueue(); - - WithTheRange(0, 10).Run(queue.Enqueue); - PerformTimes(5, queue.Dequeue); - WithTheRange(10, 15).Run(queue.Enqueue); - - OrderOfElementsIn(queue).Should().Equal(5, 6, 7, 8, 9, 10, 11, 12, 13, 14); - } - - [Fact] - public void ShouldThrowExceptionWhenDequeuingAfterInserting() - { - var queue = CreateQueue(); - - queue.Enqueue(1); - queue.Insert(0, 99); - PerformTimes(2, queue.Dequeue); - Action action = () => queue.Dequeue(); - - action.ShouldThrow(); - } - - [Fact] - public void ShouldCorrectlyDequeueElementsWhenInserting() - { - var queue = CreateQueue(); - - WithTheRange(0, 10).Run(queue.Enqueue); - queue.Insert(5, 99); - - OrderOfElementsIn(queue).Should().Equal(0, 1, 2, 3, 4, 99, 5, 6, 7, 8, 9); - } - - private static InsertionQueue CreateQueue() - { - return new InsertionQueue(1); - } - - private IEnumerable WithTheRange(int from, int to) - { - return Enumerable.Range(@from, to - @from); - } - - private IEnumerable OrderOfElementsIn(InsertionQueue queue) - { - while (true) - { - if (queue.Count == 0) - { - yield break; - } - yield return queue.Dequeue(); - } - } - - private void PerformTimes(int times, Func func) - { - WithTheRange(0, times).Run(x => func()); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using YamlDotNet.Core; + +namespace YamlDotNet.Test.Core +{ + public class InsertionQueueTests + { + private readonly ITestOutputHelper output; + + public InsertionQueueTests(ITestOutputHelper output) + { + this.output = output; + } + + [Theory] + [InlineData("-43210--", 0, "-43210X-")] + [InlineData("---43210", 0, "X--43210")] + [InlineData("10---432", 0, "10X--432")] + + [InlineData("--43210-", 5, "-X43210-")] + [InlineData("43210---", 5, "43210--X")] + [InlineData("210---43", 5, "210--X43")] + + [InlineData("-43210--", 2, "-432X10-")] + [InlineData("---43210", 2, "--432X10")] + [InlineData("43210---", 2, "432X10--")] + [InlineData("210---43", 2, "2X10--43")] + [InlineData("10---432", 2, "10--432X")] + + [InlineData("-43210--", 4, "4X3210--")] + public void CalculateInsertionParameters_is_correct(string initialState, int index, string expectedFinalState) + { + static (int capacity, int readPtr, int writePtr, int insertPtr, List<(char chr, int idx)> elements) ParseState(string state) + { + var chars = state.ToArray() + .Select((chr, idx) => (chr, idx)) + .ToList(); + + var elements = chars + .Where(e => e.chr != '-') + .ToList(); + + var insertPtr = chars + .Where(e => e.chr == 'X') + .Select(e => e.idx) + .FirstOrDefault(); + + var readPtr = chars + .SkipWhile(e => e.chr == '-') + .TakeWhile(e => e.chr != '-') + .Last() + .idx; + + var writePtr = chars + .SkipWhile(e => e.chr != '-') + .TakeWhile(e => e.chr == '-') + .Last() + .idx; + + return (state.Length, readPtr, writePtr, insertPtr, elements); + } + + var (capacity, readPtr, writePtr, _, initialElements) = ParseState(initialState); + var (finalCapacity, expectedReadPtr, expectedWritePtr, expectedInsertPtr, finalElements) = ParseState(expectedFinalState); + + if (capacity != finalCapacity) + { + throw new InvalidOperationException($"Invalid test data: capacity: {capacity}, finalCapacity: {finalCapacity}"); + } + + var capacityIsPowerOf2 = + (int)(Math.Ceiling((Math.Log(capacity) / Math.Log(2)))) == + (int)(Math.Floor(((Math.Log(capacity) / Math.Log(2))))); + + if (!capacityIsPowerOf2) + { + throw new InvalidOperationException($"Capacity should be a power of 2, but was {capacity}."); + } + + output.WriteLine("Initial State"); + output.WriteLine("============="); + output.WriteLine(""); + PrintChars((readPtr, 'R'), (writePtr, 'W')); + output.WriteLine(initialState); + output.WriteLine(""); + + output.WriteLine("Expected Final State"); + output.WriteLine("===================="); + output.WriteLine(""); + PrintChars((expectedReadPtr, 'R'), (expectedWritePtr, 'W')); + output.WriteLine(expectedFinalState); + PrintChars((expectedInsertPtr, '^')); + output.WriteLine(""); + + var movedElements = initialElements + .Join(finalElements, e => e.chr, e => e.chr, (i, f) => (i.chr, from: i.idx, offset: f.idx - i.idx)) + .Where(c => c.offset != 0) + .ToList(); + + int expectedCopyIndex, expectedCopyOffset, expectedCopyLength; + if (movedElements.Count != 0) + { + if (movedElements.Select(e => e.offset).Distinct().Count() != 1) + { + throw new InvalidOperationException("Invalid test data"); + } + + expectedCopyIndex = movedElements.Select(e => e.from).Min(); + expectedCopyOffset = movedElements[0].offset; + expectedCopyLength = movedElements.Count; + } + else + { + expectedCopyIndex = 0; + expectedCopyOffset = 0; + expectedCopyLength = 0; + } + + var mask = capacity - 1; // Assuming that capacity is a power of 2 + + InsertionQueue.CalculateInsertionParameters(mask, initialElements.Count, index, ref readPtr, ref writePtr, out var insertPtr, out var copyIndex, out var copyOffset, out var copyLength); + + static string Format(int readPtr, int writePtr, int insertPtr, int copyIndex, int copyOffset, int copyLength) + { + return $"readPtr: {readPtr}, writePtr: {writePtr}, insertPtr: {insertPtr}, copyIndex: {copyIndex}, copyOffset: {copyOffset}, copyLength: {copyLength}"; + } + + var expected = Format(expectedReadPtr, expectedWritePtr, expectedInsertPtr, expectedCopyIndex, expectedCopyOffset, expectedCopyLength); + var actual = Format(readPtr, writePtr, insertPtr, copyIndex, copyOffset, copyLength); + + output.WriteLine($"expected: {expected}"); + output.WriteLine($"actual: {actual}"); + + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(0, 4)] + [InlineData(1, 4)] + [InlineData(2, 4)] + [InlineData(3, 4)] + public void Resize_is_is_correct_when_enqueuing(int offsetBeforeResize, int initialCapacity) + { + var sut = new InsertionQueue(initialCapacity); + + for (var i = 0; i < offsetBeforeResize; ++i) + { + sut.Enqueue(-1); + sut.Dequeue(); + } + + for (var i = 0; i < initialCapacity; ++i) + { + sut.Enqueue(i + 1); + } + + // Sanity checks + Assert.Equal(initialCapacity, sut.Capacity); + Assert.Equal(Enumerable.Range(1, initialCapacity), sut); + + sut.Enqueue(initialCapacity + 1); + + Assert.Equal(initialCapacity * 2, sut.Capacity); + Assert.Equal(Enumerable.Range(1, initialCapacity + 1), sut); + } + + [Theory] + [InlineData(0, 0, 4)] + [InlineData(0, 1, 4)] + [InlineData(0, 2, 4)] + [InlineData(0, 3, 4)] + [InlineData(0, 4, 4)] + [InlineData(1, 0, 4)] + [InlineData(1, 1, 4)] + [InlineData(1, 2, 4)] + [InlineData(1, 3, 4)] + [InlineData(1, 4, 4)] + [InlineData(2, 0, 4)] + [InlineData(2, 1, 4)] + [InlineData(2, 2, 4)] + [InlineData(2, 3, 4)] + [InlineData(2, 4, 4)] + [InlineData(3, 0, 4)] + [InlineData(3, 1, 4)] + [InlineData(3, 2, 4)] + [InlineData(3, 3, 4)] + [InlineData(3, 4, 4)] + public void Resize_is_is_correct_when_inserting(int offsetBeforeResize, int insertionIndex, int initialCapacity) + { + var sut = new InsertionQueue(initialCapacity); + + for (var i = 0; i < offsetBeforeResize; ++i) + { + sut.Enqueue(-1); + sut.Dequeue(); + } + + for (var i = 0; i < initialCapacity; ++i) + { + sut.Enqueue(i + 1); + } + + // Sanity checks + Assert.Equal(initialCapacity, sut.Capacity); + Assert.Equal(Enumerable.Range(1, initialCapacity), sut); + + sut.Insert(insertionIndex, initialCapacity + 1); + + Assert.Equal(initialCapacity * 2, sut.Capacity); + + var expectedSequence = + Enumerable.Range(1, insertionIndex) + .Concat(new[] { initialCapacity + 1 }) + .Concat(Enumerable.Range(insertionIndex + 1, initialCapacity - insertionIndex)); + + Assert.Equal(expectedSequence, sut); + + sut.Enqueue(-1); + } + + private void PrintChars(params (int idx, char chr)[] characters) + { + var text = new char[characters.Max(c => c.idx) + 1]; + for (var i = 0; i < text.Length; ++i) + { + text[i] = ' '; + } + + foreach (var (idx, chr) in characters) + { + text[idx] = chr; + } + + output.WriteLine(new string(text)); + } + + [Fact] + public void ShouldThrowExceptionWhenDequeuingEmptyContainer() + { + var queue = CreateQueue(); + + Action action = () => queue.Dequeue(); + + action.ShouldThrow(); + } + + [Fact] + public void ShouldThrowExceptionWhenDequeuingContainerThatBecomesEmpty() + { + var queue = CreateQueue(); + + queue.Enqueue(1); + queue.Dequeue(); + Action action = () => queue.Dequeue(); + + action.ShouldThrow(); + } + + [Fact] + public void ShouldCorrectlyDequeueElementsAfterEnqueuing() + { + var queue = CreateQueue(); + + WithTheRange(0, 10).Run(queue.Enqueue); + + OrderOfElementsIn(queue).Should().Equal(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + } + + [Fact] + public void ShouldCorrectlyDequeueElementsWhenIntermixingEnqueuing() + { + var queue = CreateQueue(); + + WithTheRange(0, 10).Run(queue.Enqueue); + PerformTimes(5, queue.Dequeue); + WithTheRange(10, 15).Run(queue.Enqueue); + + OrderOfElementsIn(queue).Should().Equal(5, 6, 7, 8, 9, 10, 11, 12, 13, 14); + } + + [Fact] + public void ShouldThrowExceptionWhenDequeuingAfterInserting() + { + var queue = CreateQueue(); + + queue.Enqueue(1); + queue.Insert(0, 99); + PerformTimes(2, queue.Dequeue); + Action action = () => queue.Dequeue(); + + action.ShouldThrow(); + } + + [Fact] + public void ShouldCorrectlyDequeueElementsWhenInserting() + { + var queue = CreateQueue(); + + WithTheRange(0, 10).Run(queue.Enqueue); + queue.Insert(5, 99); + + OrderOfElementsIn(queue).Should().Equal(0, 1, 2, 3, 4, 99, 5, 6, 7, 8, 9); + } + + private static InsertionQueue CreateQueue() + { + return new InsertionQueue(1); + } + + private IEnumerable WithTheRange(int from, int to) + { + return Enumerable.Range(@from, to - @from); + } + + private IEnumerable OrderOfElementsIn(InsertionQueue queue) + { + while (true) + { + if (queue.Count == 0) + { + yield break; + } + yield return queue.Dequeue(); + } + } + + private void PerformTimes(int times, Func func) + { + WithTheRange(0, times).Run(x => func()); + } + } +} diff --git a/YamlDotNet.Test/Core/LookAheadBufferTests.cs b/YamlDotNet.Test/Core/LookAheadBufferTests.cs index da1882cfe..16fc4d318 100644 --- a/YamlDotNet.Test/Core/LookAheadBufferTests.cs +++ b/YamlDotNet.Test/Core/LookAheadBufferTests.cs @@ -1,217 +1,217 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using FluentAssertions; -using Xunit; -using YamlDotNet.Core; - -namespace YamlDotNet.Test.Core -{ - public class LookAheadBufferTests - { - private const string TestString = "abcdefghi"; - private const int Capacity = 4; - - [Fact] - public void ShouldHaveReadOnceWhenPeekingAtOffsetZero() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(0).Should().Be('a'); - } - - [Fact] - public void ShouldHaveReadTwiceWhenPeekingAtOffsetOne() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(0); - - buffer.Peek(1).Should().Be('b'); - } - - [Fact] - public void ShouldHaveReadThriceWhenPeekingAtOffsetTwo() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(0); - buffer.Peek(1); - - buffer.Peek(2).Should().Be('c'); - } - - [Fact] - public void ShouldNotHaveReadAfterSkippingOneCharacter() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(2); - - buffer.Skip(1); - - buffer.Peek(0).Should().Be('b'); - buffer.Peek(1).Should().Be('c'); - } - - [Fact] - public void ShouldHaveReadOnceAfterSkippingOneCharacter() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(2); - - buffer.Skip(1); - buffer.Peek(2).Should().Be('d'); - } - - [Fact] - public void ShouldHaveReadTwiceAfterSkippingOneCharacter() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(2); - - buffer.Skip(1); - buffer.Peek(3).Should().Be('e'); - } - - [Fact] - public void ShouldHaveReadOnceAfterSkippingFiveCharacters() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(2); - buffer.Skip(1); - buffer.Peek(3); - - buffer.Skip(4); - buffer.Peek(0).Should().Be('f'); - } - - [Fact] - public void ShouldHaveReadOnceAfterSkippingSixCharacters() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(2); - buffer.Skip(1); - buffer.Peek(3); - buffer.Skip(4); - buffer.Peek(0); - - buffer.Skip(1); - buffer.Peek(0).Should().Be('g'); - } - - [Fact] - public void ShouldHaveReadOnceAfterSkippingSevenCharacters() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(2); - buffer.Skip(1); - buffer.Peek(3); - buffer.Skip(4); - buffer.Peek(1); - - buffer.Skip(2); - buffer.Peek(0).Should().Be('h'); - } - - [Fact] - public void ShouldHaveReadOnceAfterSkippingEightCharacters() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(2); - buffer.Skip(1); - buffer.Peek(3); - buffer.Skip(4); - buffer.Peek(2); - - buffer.Skip(3); - buffer.Peek(0).Should().Be('i'); - } - - [Fact] - public void ShouldHaveReadOnceAfterSkippingNineCharacters() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(2); - buffer.Skip(1); - buffer.Peek(3); - buffer.Skip(4); - buffer.Peek(3); - - buffer.Skip(4); - buffer.Peek(0).Should().Be('\0'); - } - - [Fact] - public void ShouldFindEndOfInput() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(2); - buffer.Skip(1); - buffer.Peek(3); - buffer.Skip(4); - buffer.Peek(3); - buffer.Skip(4); - buffer.Peek(0); - - buffer.EndOfInput.Should().BeTrue(); - } - - [Fact] - public void ShouldThrowWhenSkippingBeyondCurrentBuffer() - { - var reader = new StringReader(TestString); - var buffer = CreateBuffer(reader, Capacity); - - buffer.Peek(3); - Action action = () => buffer.Skip(5); - - action.ShouldThrow(); - } - - private static LookAheadBuffer CreateBuffer(TextReader reader, int capacity) - { - return new LookAheadBuffer(reader, capacity); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.IO; +using FluentAssertions; +using Xunit; +using YamlDotNet.Core; + +namespace YamlDotNet.Test.Core +{ + public class LookAheadBufferTests + { + private const string TestString = "abcdefghi"; + private const int Capacity = 4; + + [Fact] + public void ShouldHaveReadOnceWhenPeekingAtOffsetZero() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(0).Should().Be('a'); + } + + [Fact] + public void ShouldHaveReadTwiceWhenPeekingAtOffsetOne() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(0); + + buffer.Peek(1).Should().Be('b'); + } + + [Fact] + public void ShouldHaveReadThriceWhenPeekingAtOffsetTwo() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(0); + buffer.Peek(1); + + buffer.Peek(2).Should().Be('c'); + } + + [Fact] + public void ShouldNotHaveReadAfterSkippingOneCharacter() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(2); + + buffer.Skip(1); + + buffer.Peek(0).Should().Be('b'); + buffer.Peek(1).Should().Be('c'); + } + + [Fact] + public void ShouldHaveReadOnceAfterSkippingOneCharacter() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(2); + + buffer.Skip(1); + buffer.Peek(2).Should().Be('d'); + } + + [Fact] + public void ShouldHaveReadTwiceAfterSkippingOneCharacter() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(2); + + buffer.Skip(1); + buffer.Peek(3).Should().Be('e'); + } + + [Fact] + public void ShouldHaveReadOnceAfterSkippingFiveCharacters() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(2); + buffer.Skip(1); + buffer.Peek(3); + + buffer.Skip(4); + buffer.Peek(0).Should().Be('f'); + } + + [Fact] + public void ShouldHaveReadOnceAfterSkippingSixCharacters() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(2); + buffer.Skip(1); + buffer.Peek(3); + buffer.Skip(4); + buffer.Peek(0); + + buffer.Skip(1); + buffer.Peek(0).Should().Be('g'); + } + + [Fact] + public void ShouldHaveReadOnceAfterSkippingSevenCharacters() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(2); + buffer.Skip(1); + buffer.Peek(3); + buffer.Skip(4); + buffer.Peek(1); + + buffer.Skip(2); + buffer.Peek(0).Should().Be('h'); + } + + [Fact] + public void ShouldHaveReadOnceAfterSkippingEightCharacters() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(2); + buffer.Skip(1); + buffer.Peek(3); + buffer.Skip(4); + buffer.Peek(2); + + buffer.Skip(3); + buffer.Peek(0).Should().Be('i'); + } + + [Fact] + public void ShouldHaveReadOnceAfterSkippingNineCharacters() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(2); + buffer.Skip(1); + buffer.Peek(3); + buffer.Skip(4); + buffer.Peek(3); + + buffer.Skip(4); + buffer.Peek(0).Should().Be('\0'); + } + + [Fact] + public void ShouldFindEndOfInput() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(2); + buffer.Skip(1); + buffer.Peek(3); + buffer.Skip(4); + buffer.Peek(3); + buffer.Skip(4); + buffer.Peek(0); + + buffer.EndOfInput.Should().BeTrue(); + } + + [Fact] + public void ShouldThrowWhenSkippingBeyondCurrentBuffer() + { + var reader = new StringReader(TestString); + var buffer = CreateBuffer(reader, Capacity); + + buffer.Peek(3); + Action action = () => buffer.Skip(5); + + action.ShouldThrow(); + } + + private static LookAheadBuffer CreateBuffer(TextReader reader, int capacity) + { + return new LookAheadBuffer(reader, capacity); + } + } +} diff --git a/YamlDotNet.Test/Core/MarkCursorTests.cs b/YamlDotNet.Test/Core/MarkCursorTests.cs index 2d5900a94..1a9bdf9f0 100644 --- a/YamlDotNet.Test/Core/MarkCursorTests.cs +++ b/YamlDotNet.Test/Core/MarkCursorTests.cs @@ -1,41 +1,41 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using FluentAssertions; -using Xunit; -using YamlDotNet.Core; - -namespace YamlDotNet.Test.Core -{ - public class MarkCursorTests - { - [Fact] - public void ShouldProvideAnOneIndexedMark() - { - var cursor = new Cursor(); - - var result = cursor.Mark(); - - result.Line.Should().Be(1, "the mark should be at line 1"); - result.Column.Should().Be(1, "the mark should be at column 1"); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using FluentAssertions; +using Xunit; +using YamlDotNet.Core; + +namespace YamlDotNet.Test.Core +{ + public class MarkCursorTests + { + [Fact] + public void ShouldProvideAnOneIndexedMark() + { + var cursor = new Cursor(); + + var result = cursor.Mark(); + + result.Line.Should().Be(1, "the mark should be at line 1"); + result.Column.Should().Be(1, "the mark should be at column 1"); + } + } +} diff --git a/YamlDotNet.Test/Core/ParserTests.cs b/YamlDotNet.Test/Core/ParserTests.cs index ba423e458..23337e903 100644 --- a/YamlDotNet.Test/Core/ParserTests.cs +++ b/YamlDotNet.Test/Core/ParserTests.cs @@ -1,459 +1,459 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Xunit; -using YamlDotNet.Core; - -namespace YamlDotNet.Test.Core -{ - public class ParserTests : EventsHelper - { - [Fact] - public void EmptyDocument() - { - AssertSequenceOfEventsFrom(Yaml.ParserForEmptyContent(), - StreamStart, - StreamEnd); - } - - [Fact] - public void VerifyEventsOnExample1() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("01-directives.yaml"), - StreamStart, - DocumentStart(Explicit, Version(1, 1), - TagDirective("!", "!foo"), - TagDirective("!yaml!", TagYaml), - TagDirective("!!", TagYaml)), - PlainScalar(string.Empty), - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample2() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("02-scalar-in-imp-doc.yaml"), - StreamStart, - DocumentStart(Implicit), - SingleQuotedScalar("a scalar"), - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample3() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("03-scalar-in-exp-doc.yaml"), - StreamStart, - DocumentStart(Explicit), - SingleQuotedScalar("a scalar"), - DocumentEnd(Explicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample4() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("04-scalars-in-multi-docs.yaml"), - StreamStart, - DocumentStart(Implicit), - SingleQuotedScalar("a scalar"), - DocumentEnd(Implicit), - DocumentStart(Explicit), - SingleQuotedScalar("another scalar"), - DocumentEnd(Implicit), - DocumentStart(Explicit), - SingleQuotedScalar("yet another scalar"), - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample5() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("05-circular-sequence.yaml"), - StreamStart, - DocumentStart(Implicit), - FlowSequenceStart.A("A"), - AnchorAlias("A"), - SequenceEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample6() - { - var parser = Yaml.ParserForResource("06-float-tag.yaml"); - AssertSequenceOfEventsFrom(parser, - StreamStart, - DocumentStart(Implicit), - DoubleQuotedScalar("3.14").T(TagYaml + "float"), - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample7() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("07-scalar-styles.yaml"), - StreamStart, - DocumentStart(Explicit), - PlainScalar(string.Empty), - DocumentEnd(Implicit), - DocumentStart(Explicit), - PlainScalar("a plain scalar"), - DocumentEnd(Implicit), - DocumentStart(Explicit), - SingleQuotedScalar("a single-quoted scalar"), - DocumentEnd(Implicit), - DocumentStart(Explicit), - DoubleQuotedScalar("a double-quoted scalar"), - DocumentEnd(Implicit), - DocumentStart(Explicit), - LiteralScalar("a literal scalar"), - DocumentEnd(Implicit), - DocumentStart(Explicit), - FoldedScalar("a folded scalar"), - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample8() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("08-flow-sequence.yaml"), - StreamStart, - DocumentStart(Implicit), - FlowSequenceStart, - PlainScalar("item 1"), - PlainScalar("item 2"), - PlainScalar("item 3"), - SequenceEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample9() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("09-flow-mapping.yaml"), - StreamStart, - DocumentStart(Implicit), - FlowMappingStart, - PlainScalar("a simple key"), - PlainScalar("a value"), - PlainScalar("a complex key"), - PlainScalar("another value"), - MappingEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample10() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("10-mixed-nodes-in-sequence.yaml"), - StreamStart, - DocumentStart(Implicit), - BlockSequenceStart, - PlainScalar("item 1"), - PlainScalar("item 2"), - BlockSequenceStart, - PlainScalar("item 3.1"), - PlainScalar("item 3.2"), - SequenceEnd, - BlockMappingStart, - PlainScalar("key 1"), - PlainScalar("value 1"), - PlainScalar("key 2"), - PlainScalar("value 2"), - MappingEnd, - SequenceEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample11() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("11-mixed-nodes-in-mapping.yaml"), - StreamStart, - DocumentStart(Implicit), - BlockMappingStart, - PlainScalar("a simple key"), - PlainScalar("a value"), - PlainScalar("a complex key"), - PlainScalar("another value"), - PlainScalar("a mapping"), - BlockMappingStart, - PlainScalar("key 1"), - PlainScalar("value 1"), - PlainScalar("key 2"), - PlainScalar("value 2"), - MappingEnd, - PlainScalar("a sequence"), - BlockSequenceStart, - PlainScalar("item 1"), - PlainScalar("item 2"), - SequenceEnd, - MappingEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample12() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("12-compact-sequence.yaml"), - StreamStart, - DocumentStart(Implicit), - BlockSequenceStart, - BlockSequenceStart, - PlainScalar("item 1"), - PlainScalar("item 2"), - SequenceEnd, - BlockMappingStart, - PlainScalar("key 1"), - PlainScalar("value 1"), - PlainScalar("key 2"), - PlainScalar("value 2"), - MappingEnd, - BlockMappingStart, - PlainScalar("complex key"), - PlainScalar("complex value"), - MappingEnd, - SequenceEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample13() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("13-compact-mapping.yaml"), - StreamStart, - DocumentStart(Implicit), - BlockMappingStart, - PlainScalar("a sequence"), - BlockSequenceStart, - PlainScalar("item 1"), - PlainScalar("item 2"), - SequenceEnd, - PlainScalar("a mapping"), - BlockMappingStart, - PlainScalar("key 1"), - PlainScalar("value 1"), - PlainScalar("key 2"), - PlainScalar("value 2"), - MappingEnd, - MappingEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample14() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("14-mapping-wo-indent.yaml"), - StreamStart, - DocumentStart(Implicit), - BlockMappingStart, - PlainScalar("key"), - BlockSequenceStart, - PlainScalar("item 1"), - PlainScalar("item 2"), - SequenceEnd, - MappingEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokenWithLocalTags() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("local-tags.yaml"), - StreamStart, - DocumentStart(Explicit), - BlockMappingStart.T("!MyObject").Explicit, - PlainScalar("a"), - PlainScalar("1.0"), - PlainScalar("b"), - PlainScalar("42"), - PlainScalar("c"), - PlainScalar("-7"), - MappingEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void CommentsAreReturnedWhenRequested() - { - AssertSequenceOfEventsFrom(new Parser(new Scanner(Yaml.ReaderForText(@" - # Top comment - - first # Comment on first item - - second - - # a mapping - ? key # my key - : value # my value - # Bottom comment - "), skipComments: false)), - StreamStart, - DocumentStart(Implicit), - StandaloneComment("Top comment"), - BlockSequenceStart, - PlainScalar("first"), - InlineComment("Comment on first item"), - PlainScalar("second"), - InlineComment("a mapping"), - BlockMappingStart, - PlainScalar("key"), - InlineComment("my key"), - PlainScalar("value"), - InlineComment("my value"), - StandaloneComment("Bottom comment"), - MappingEnd, - SequenceEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void CommentsDoNotInfluenceParsing_Issue_260() - { - AssertSequenceOfEventsFrom(new Parser(new Scanner(Yaml.ReaderForText(@" - # Some comment - key: value - "), skipComments: false)), - StreamStart, - DocumentStart(Implicit), - StandaloneComment("Some comment"), - BlockMappingStart, - PlainScalar("key"), - PlainScalar("value"), - MappingEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void CommentsAreOmittedUnlessRequested() - { - AssertSequenceOfEventsFrom(Yaml.ParserForText(@" - # Top comment - - first # Comment on first item - - second - - # a mapping - ? key # my key - : value # my value - # Bottom comment - "), - StreamStart, - DocumentStart(Implicit), - BlockSequenceStart, - PlainScalar("first"), - PlainScalar("second"), - BlockMappingStart, - PlainScalar("key"), - PlainScalar("value"), - MappingEnd, - SequenceEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokenWithMultiDocTag() - { - AssertSequenceOfEventsFrom(Yaml.ParserForResource("multi-doc-tag.yaml"), - StreamStart, - DocumentStart(Explicit, Version(1, 1), - TagDirective("!x!", "tag:example.com,2014:"), - TagDirective("!", "!"), - TagDirective("!!", TagYaml)), - BlockMappingStart.T("tag:example.com,2014:foo").Explicit, - PlainScalar("x"), - PlainScalar("0"), - MappingEnd, - DocumentEnd(Implicit), - DocumentStart(Explicit), - BlockMappingStart.T("tag:example.com,2014:bar").Explicit, - PlainScalar("x"), - PlainScalar("1"), - MappingEnd, - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokenWithUrlEncodedTagContainingPlusSpaces() - { - AssertSequenceOfEventsFrom(Yaml.ParserForText("!(%20%20%20hello+you%20+) value"), - StreamStart, - DocumentStart(Implicit), - PlainScalar("value").T("!( hello you )"), - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyTokenWithUrlEncoded32BitsUnicodeTags() - { - AssertSequenceOfEventsFrom(Yaml.ParserForText("!hel%F4%8F%BF%BFlo%E2%99%A5+A%20 value"), - StreamStart, - DocumentStart(Implicit), - PlainScalar("value").T("!hel􏿿lo♥ A "), - DocumentEnd(Implicit), - StreamEnd); - } - - [Fact] - public void VerifyCommentTypeAfterScalarBlock() - { - AssertSequenceOfEventsFrom(new Parser(new Scanner(Yaml.ReaderForText("|-\r\n text\r\n#comment"), false)), - StreamStart, - DocumentStart(Implicit), - LiteralScalar("text"), - StandaloneComment("comment"), - DocumentEnd(Implicit), - StreamEnd); - } - - [Theory] - [InlineData("|\n b-carriage-return,b-line-feed\r\n lll", "b-carriage-return,b-line-feed\nlll")] - [InlineData("|\n b-carriage-return\r lll", "b-carriage-return\nlll")] - [InlineData("|\n b-line-feed\n lll", "b-line-feed\nlll")] - [InlineData("|\n b-next-line\x85 lll", "b-next-line\nlll")] - [InlineData("|\n b-line-separator\x2028 lll", "b-line-separator\x2028lll")] - [InlineData("|\n b-paragraph-separator\x2029 lll", "b-paragraph-separator\x2029lll")] - public void NewLinesAreParsedAccordingToTheSpecification(string yaml, string expected) - { - AssertSequenceOfEventsFrom(Yaml.ParserForText(yaml), - StreamStart, - DocumentStart(Implicit), - LiteralScalar(expected), - DocumentEnd(Implicit), - StreamEnd); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using Xunit; +using YamlDotNet.Core; + +namespace YamlDotNet.Test.Core +{ + public class ParserTests : EventsHelper + { + [Fact] + public void EmptyDocument() + { + AssertSequenceOfEventsFrom(Yaml.ParserForEmptyContent(), + StreamStart, + StreamEnd); + } + + [Fact] + public void VerifyEventsOnExample1() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("01-directives.yaml"), + StreamStart, + DocumentStart(Explicit, Version(1, 1), + TagDirective("!", "!foo"), + TagDirective("!yaml!", TagYaml), + TagDirective("!!", TagYaml)), + PlainScalar(string.Empty), + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample2() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("02-scalar-in-imp-doc.yaml"), + StreamStart, + DocumentStart(Implicit), + SingleQuotedScalar("a scalar"), + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample3() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("03-scalar-in-exp-doc.yaml"), + StreamStart, + DocumentStart(Explicit), + SingleQuotedScalar("a scalar"), + DocumentEnd(Explicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample4() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("04-scalars-in-multi-docs.yaml"), + StreamStart, + DocumentStart(Implicit), + SingleQuotedScalar("a scalar"), + DocumentEnd(Implicit), + DocumentStart(Explicit), + SingleQuotedScalar("another scalar"), + DocumentEnd(Implicit), + DocumentStart(Explicit), + SingleQuotedScalar("yet another scalar"), + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample5() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("05-circular-sequence.yaml"), + StreamStart, + DocumentStart(Implicit), + FlowSequenceStart.A("A"), + AnchorAlias("A"), + SequenceEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample6() + { + var parser = Yaml.ParserForResource("06-float-tag.yaml"); + AssertSequenceOfEventsFrom(parser, + StreamStart, + DocumentStart(Implicit), + DoubleQuotedScalar("3.14").T(TagYaml + "float"), + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample7() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("07-scalar-styles.yaml"), + StreamStart, + DocumentStart(Explicit), + PlainScalar(string.Empty), + DocumentEnd(Implicit), + DocumentStart(Explicit), + PlainScalar("a plain scalar"), + DocumentEnd(Implicit), + DocumentStart(Explicit), + SingleQuotedScalar("a single-quoted scalar"), + DocumentEnd(Implicit), + DocumentStart(Explicit), + DoubleQuotedScalar("a double-quoted scalar"), + DocumentEnd(Implicit), + DocumentStart(Explicit), + LiteralScalar("a literal scalar"), + DocumentEnd(Implicit), + DocumentStart(Explicit), + FoldedScalar("a folded scalar"), + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample8() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("08-flow-sequence.yaml"), + StreamStart, + DocumentStart(Implicit), + FlowSequenceStart, + PlainScalar("item 1"), + PlainScalar("item 2"), + PlainScalar("item 3"), + SequenceEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample9() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("09-flow-mapping.yaml"), + StreamStart, + DocumentStart(Implicit), + FlowMappingStart, + PlainScalar("a simple key"), + PlainScalar("a value"), + PlainScalar("a complex key"), + PlainScalar("another value"), + MappingEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample10() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("10-mixed-nodes-in-sequence.yaml"), + StreamStart, + DocumentStart(Implicit), + BlockSequenceStart, + PlainScalar("item 1"), + PlainScalar("item 2"), + BlockSequenceStart, + PlainScalar("item 3.1"), + PlainScalar("item 3.2"), + SequenceEnd, + BlockMappingStart, + PlainScalar("key 1"), + PlainScalar("value 1"), + PlainScalar("key 2"), + PlainScalar("value 2"), + MappingEnd, + SequenceEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample11() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("11-mixed-nodes-in-mapping.yaml"), + StreamStart, + DocumentStart(Implicit), + BlockMappingStart, + PlainScalar("a simple key"), + PlainScalar("a value"), + PlainScalar("a complex key"), + PlainScalar("another value"), + PlainScalar("a mapping"), + BlockMappingStart, + PlainScalar("key 1"), + PlainScalar("value 1"), + PlainScalar("key 2"), + PlainScalar("value 2"), + MappingEnd, + PlainScalar("a sequence"), + BlockSequenceStart, + PlainScalar("item 1"), + PlainScalar("item 2"), + SequenceEnd, + MappingEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample12() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("12-compact-sequence.yaml"), + StreamStart, + DocumentStart(Implicit), + BlockSequenceStart, + BlockSequenceStart, + PlainScalar("item 1"), + PlainScalar("item 2"), + SequenceEnd, + BlockMappingStart, + PlainScalar("key 1"), + PlainScalar("value 1"), + PlainScalar("key 2"), + PlainScalar("value 2"), + MappingEnd, + BlockMappingStart, + PlainScalar("complex key"), + PlainScalar("complex value"), + MappingEnd, + SequenceEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample13() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("13-compact-mapping.yaml"), + StreamStart, + DocumentStart(Implicit), + BlockMappingStart, + PlainScalar("a sequence"), + BlockSequenceStart, + PlainScalar("item 1"), + PlainScalar("item 2"), + SequenceEnd, + PlainScalar("a mapping"), + BlockMappingStart, + PlainScalar("key 1"), + PlainScalar("value 1"), + PlainScalar("key 2"), + PlainScalar("value 2"), + MappingEnd, + MappingEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample14() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("14-mapping-wo-indent.yaml"), + StreamStart, + DocumentStart(Implicit), + BlockMappingStart, + PlainScalar("key"), + BlockSequenceStart, + PlainScalar("item 1"), + PlainScalar("item 2"), + SequenceEnd, + MappingEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokenWithLocalTags() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("local-tags.yaml"), + StreamStart, + DocumentStart(Explicit), + BlockMappingStart.T("!MyObject").Explicit, + PlainScalar("a"), + PlainScalar("1.0"), + PlainScalar("b"), + PlainScalar("42"), + PlainScalar("c"), + PlainScalar("-7"), + MappingEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void CommentsAreReturnedWhenRequested() + { + AssertSequenceOfEventsFrom(new Parser(new Scanner(Yaml.ReaderForText(@" + # Top comment + - first # Comment on first item + - second + - # a mapping + ? key # my key + : value # my value + # Bottom comment + "), skipComments: false)), + StreamStart, + DocumentStart(Implicit), + StandaloneComment("Top comment"), + BlockSequenceStart, + PlainScalar("first"), + InlineComment("Comment on first item"), + PlainScalar("second"), + InlineComment("a mapping"), + BlockMappingStart, + PlainScalar("key"), + InlineComment("my key"), + PlainScalar("value"), + InlineComment("my value"), + StandaloneComment("Bottom comment"), + MappingEnd, + SequenceEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void CommentsDoNotInfluenceParsing_Issue_260() + { + AssertSequenceOfEventsFrom(new Parser(new Scanner(Yaml.ReaderForText(@" + # Some comment + key: value + "), skipComments: false)), + StreamStart, + DocumentStart(Implicit), + StandaloneComment("Some comment"), + BlockMappingStart, + PlainScalar("key"), + PlainScalar("value"), + MappingEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void CommentsAreOmittedUnlessRequested() + { + AssertSequenceOfEventsFrom(Yaml.ParserForText(@" + # Top comment + - first # Comment on first item + - second + - # a mapping + ? key # my key + : value # my value + # Bottom comment + "), + StreamStart, + DocumentStart(Implicit), + BlockSequenceStart, + PlainScalar("first"), + PlainScalar("second"), + BlockMappingStart, + PlainScalar("key"), + PlainScalar("value"), + MappingEnd, + SequenceEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokenWithMultiDocTag() + { + AssertSequenceOfEventsFrom(Yaml.ParserForResource("multi-doc-tag.yaml"), + StreamStart, + DocumentStart(Explicit, Version(1, 1), + TagDirective("!x!", "tag:example.com,2014:"), + TagDirective("!", "!"), + TagDirective("!!", TagYaml)), + BlockMappingStart.T("tag:example.com,2014:foo").Explicit, + PlainScalar("x"), + PlainScalar("0"), + MappingEnd, + DocumentEnd(Implicit), + DocumentStart(Explicit), + BlockMappingStart.T("tag:example.com,2014:bar").Explicit, + PlainScalar("x"), + PlainScalar("1"), + MappingEnd, + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokenWithUrlEncodedTagContainingPlusSpaces() + { + AssertSequenceOfEventsFrom(Yaml.ParserForText("!(%20%20%20hello+you%20+) value"), + StreamStart, + DocumentStart(Implicit), + PlainScalar("value").T("!( hello you )"), + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyTokenWithUrlEncoded32BitsUnicodeTags() + { + AssertSequenceOfEventsFrom(Yaml.ParserForText("!hel%F4%8F%BF%BFlo%E2%99%A5+A%20 value"), + StreamStart, + DocumentStart(Implicit), + PlainScalar("value").T("!hel􏿿lo♥ A "), + DocumentEnd(Implicit), + StreamEnd); + } + + [Fact] + public void VerifyCommentTypeAfterScalarBlock() + { + AssertSequenceOfEventsFrom(new Parser(new Scanner(Yaml.ReaderForText("|-\r\n text\r\n#comment"), false)), + StreamStart, + DocumentStart(Implicit), + LiteralScalar("text"), + StandaloneComment("comment"), + DocumentEnd(Implicit), + StreamEnd); + } + + [Theory] + [InlineData("|\n b-carriage-return,b-line-feed\r\n lll", "b-carriage-return,b-line-feed\nlll")] + [InlineData("|\n b-carriage-return\r lll", "b-carriage-return\nlll")] + [InlineData("|\n b-line-feed\n lll", "b-line-feed\nlll")] + [InlineData("|\n b-next-line\x85 lll", "b-next-line\nlll")] + [InlineData("|\n b-line-separator\x2028 lll", "b-line-separator\x2028lll")] + [InlineData("|\n b-paragraph-separator\x2029 lll", "b-paragraph-separator\x2029lll")] + public void NewLinesAreParsedAccordingToTheSpecification(string yaml, string expected) + { + AssertSequenceOfEventsFrom(Yaml.ParserForText(yaml), + StreamStart, + DocumentStart(Implicit), + LiteralScalar(expected), + DocumentEnd(Implicit), + StreamEnd); + } + } +} diff --git a/YamlDotNet.Test/Core/ScannerTests.cs b/YamlDotNet.Test/Core/ScannerTests.cs index 2bf85c271..9d044ba17 100644 --- a/YamlDotNet.Test/Core/ScannerTests.cs +++ b/YamlDotNet.Test/Core/ScannerTests.cs @@ -1,510 +1,510 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using FluentAssertions; -using Xunit; -using YamlDotNet.Core; -using YamlDotNet.Core.Tokens; - -namespace YamlDotNet.Test.Core -{ - public class ScannerTests : TokenHelper - { - [Fact] - public void VerifyTokensOnExample1() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("01-directives.yaml"), - StreamStart, - VersionDirective(1, 1), - TagDirective("!", "!foo"), - TagDirective("!yaml!", "tag:yaml.org,2002:"), - DocumentStart, - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample2() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("02-scalar-in-imp-doc.yaml"), - StreamStart, - SingleQuotedScalar("a scalar"), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample3() - { - var scanner = Yaml.ScannerForResource("03-scalar-in-exp-doc.yaml"); - AssertSequenceOfTokensFrom(scanner, - StreamStart, - DocumentStart, - SingleQuotedScalar("a scalar"), - DocumentEnd, - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample4() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("04-scalars-in-multi-docs.yaml"), - StreamStart, - SingleQuotedScalar("a scalar"), - DocumentStart, - SingleQuotedScalar("another scalar"), - DocumentStart, - SingleQuotedScalar("yet another scalar"), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample5() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("05-circular-sequence.yaml"), - StreamStart, - Anchor("A"), - FlowSequenceStart, - AnchorAlias("A"), - FlowSequenceEnd, - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample6() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("06-float-tag.yaml"), - StreamStart, - Tag("!!", "float"), - DoubleQuotedScalar("3.14"), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample7() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("07-scalar-styles.yaml"), - StreamStart, - DocumentStart, - DocumentStart, - PlainScalar("a plain scalar"), - DocumentStart, - SingleQuotedScalar("a single-quoted scalar"), - DocumentStart, - DoubleQuotedScalar("a double-quoted scalar"), - DocumentStart, - LiteralScalar("a literal scalar"), - DocumentStart, - FoldedScalar("a folded scalar"), - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample8() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("08-flow-sequence.yaml"), - StreamStart, - FlowSequenceStart, - PlainScalar("item 1"), - FlowEntry, - PlainScalar("item 2"), - FlowEntry, - PlainScalar("item 3"), - FlowSequenceEnd, - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample9() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("09-flow-mapping.yaml"), - StreamStart, - FlowMappingStart, - Key, - PlainScalar("a simple key"), - Value, - PlainScalar("a value"), - FlowEntry, - Key, - PlainScalar("a complex key"), - Value, - PlainScalar("another value"), - FlowEntry, - FlowMappingEnd, - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample10() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("10-mixed-nodes-in-sequence.yaml"), - StreamStart, - BlockSequenceStart, - BlockEntry, - PlainScalar("item 1"), - BlockEntry, - PlainScalar("item 2"), - BlockEntry, - BlockSequenceStart, - BlockEntry, - PlainScalar("item 3.1"), - BlockEntry, - PlainScalar("item 3.2"), - BlockEnd, - BlockEntry, - BlockMappingStart, - Key, - PlainScalar("key 1"), - Value, - PlainScalar("value 1"), - Key, - PlainScalar("key 2"), - Value, - PlainScalar("value 2"), - BlockEnd, - BlockEnd, - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample11() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("11-mixed-nodes-in-mapping.yaml"), - StreamStart, - BlockMappingStart, - Key, - PlainScalar("a simple key"), - Value, - PlainScalar("a value"), - Key, - PlainScalar("a complex key"), - Value, - PlainScalar("another value"), - Key, - PlainScalar("a mapping"), - Value, - BlockMappingStart, - Key, - PlainScalar("key 1"), - Value, - PlainScalar("value 1"), - Key, - PlainScalar("key 2"), - Value, - PlainScalar("value 2"), - BlockEnd, - Key, - PlainScalar("a sequence"), - Value, - BlockSequenceStart, - BlockEntry, - PlainScalar("item 1"), - BlockEntry, - PlainScalar("item 2"), - BlockEnd, - BlockEnd, - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample12() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("12-compact-sequence.yaml"), - StreamStart, - BlockSequenceStart, - BlockEntry, - BlockSequenceStart, - BlockEntry, - PlainScalar("item 1"), - BlockEntry, - PlainScalar("item 2"), - BlockEnd, - BlockEntry, - BlockMappingStart, - Key, - PlainScalar("key 1"), - Value, - PlainScalar("value 1"), - Key, - PlainScalar("key 2"), - Value, - PlainScalar("value 2"), - BlockEnd, - BlockEntry, - BlockMappingStart, - Key, - PlainScalar("complex key"), - Value, - PlainScalar("complex value"), - BlockEnd, - BlockEnd, - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample13() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("13-compact-mapping.yaml"), - StreamStart, - BlockMappingStart, - Key, - PlainScalar("a sequence"), - Value, - BlockSequenceStart, - BlockEntry, - PlainScalar("item 1"), - BlockEntry, - PlainScalar("item 2"), - BlockEnd, - Key, - PlainScalar("a mapping"), - Value, - BlockMappingStart, - Key, - PlainScalar("key 1"), - Value, - PlainScalar("value 1"), - Key, - PlainScalar("key 2"), - Value, - PlainScalar("value 2"), - BlockEnd, - BlockEnd, - StreamEnd); - } - - [Fact] - public void VerifyTokensOnExample14() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForResource("14-mapping-wo-indent.yaml"), - StreamStart, - BlockMappingStart, - Key, - PlainScalar("key"), - Value, - BlockEntry, - PlainScalar("item 1"), - BlockEntry, - PlainScalar("item 2"), - BlockEnd, - StreamEnd); - } - - [Fact] - public void CommentsAreReturnedWhenRequested() - { - AssertSequenceOfTokensFrom(new Scanner(Yaml.ReaderForText(@" - # Top comment - - first # Comment on first item - - second - # Bottom comment - "), skipComments: false), - StreamStart, - StandaloneComment("Top comment"), - BlockSequenceStart, - BlockEntry, - PlainScalar("first"), - InlineComment("Comment on first item"), - BlockEntry, - PlainScalar("second"), - StandaloneComment("Bottom comment"), - BlockEnd, - StreamEnd); - } - - [Fact] - public void CommentsAreCorrectlyMarked() - { - var sut = new Scanner(Yaml.ReaderForText(@" - - first # Comment on first item - "), skipComments: false); - - while (sut.MoveNext()) - { - if (sut.Current is Comment comment) - { - Assert.Equal(8, comment.Start.Index); - Assert.Equal(31, comment.End.Index); - - return; - } - } - - Assert.True(false, "Did not find a comment"); - } - - [Fact] - public void CommentsAreOmittedUnlessRequested() - { - AssertSequenceOfTokensFrom(Yaml.ScannerForText(@" - # Top comment - - first # Comment on first item - - second - # Bottom comment - "), - StreamStart, - BlockSequenceStart, - BlockEntry, - PlainScalar("first"), - BlockEntry, - PlainScalar("second"), - BlockEnd, - StreamEnd); - } - - [Fact] - public void MarksOnDoubleQuotedScalarsAreCorrect() - { - var scanner = Yaml.ScannerForText(@" - ""x"" - "); - - Scalar scalar = null; - while (scanner.MoveNext() && scalar == null) - { - scalar = scanner.Current as Scalar; - } - Assert.Equal(4, scalar.End.Column); - } - - [Fact] - public void Slow_stream_is_parsed_correctly() - { - var buffer = new MemoryStream(); - Yaml.StreamFrom("04-scalars-in-multi-docs.yaml").CopyTo(buffer); - - var slowStream = new SlowStream(buffer.ToArray()); - - var scanner = new Scanner(new StreamReader(slowStream)); - - scanner.MoveNext(); - - // Should not fail - scanner.MoveNext(); - } - - [Fact] - public void Issue_553_562() - { - var yaml = "MainItem4:\n" + string.Join("\n", Enumerable.Range(1, 100).Select(e => $"- {{item: {{foo1: {e}, foo2: 'bar{e}' }}}}")); - - var scanner = new Scanner(new StringReader(yaml)); - while (scanner.MoveNext()) - { - } - } - - private void AssertPartialSequenceOfTokensFrom(Scanner scanner, params Token[] tokens) - { - var tokenNumber = 1; - foreach (var expected in tokens) - { - scanner.MoveNext().Should().BeTrue("Missing token number {0}", tokenNumber); - AssertToken(expected, scanner.Current, tokenNumber); - tokenNumber++; - } - } - - private void AssertSequenceOfTokensFrom(Scanner scanner, params Token[] tokens) - { - AssertPartialSequenceOfTokensFrom(scanner, tokens); - scanner.MoveNext().Should().BeFalse("Found extra tokens"); - } - - private void AssertToken(Token expected, Token actual, int tokenNumber) - { - actual.Should().NotBeNull(); - actual.GetType().Should().Be(expected.GetType(), "Token {0} is not of the expected type", tokenNumber); - - foreach (var property in expected.GetType().GetTypeInfo().GetProperties()) - { - if (property.PropertyType != typeof(Mark) && property.CanRead && property.Name != "IsKey") - { - var value = property.GetValue(actual, null); - var expectedValue = property.GetValue(expected, null); - value.Should().Be(expectedValue, "Comparing property {0} in token {1}", property.Name, tokenNumber); - } - } - } - - /// - /// A stream that reads one byte at the time. - /// - public class SlowStream : Stream - { - private readonly byte[] data; - private int position; - - public SlowStream(byte[] data) - { - this.data = data; - } - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override long Length => data.Length; - - public override long Position - { - get => position; - set => throw new NotSupportedException(); - } - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (count == 0 || position == data.Length) - { - return 0; - } - - buffer[offset] = data[position]; - ++position; - return 1; - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using FluentAssertions; +using Xunit; +using YamlDotNet.Core; +using YamlDotNet.Core.Tokens; + +namespace YamlDotNet.Test.Core +{ + public class ScannerTests : TokenHelper + { + [Fact] + public void VerifyTokensOnExample1() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("01-directives.yaml"), + StreamStart, + VersionDirective(1, 1), + TagDirective("!", "!foo"), + TagDirective("!yaml!", "tag:yaml.org,2002:"), + DocumentStart, + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample2() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("02-scalar-in-imp-doc.yaml"), + StreamStart, + SingleQuotedScalar("a scalar"), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample3() + { + var scanner = Yaml.ScannerForResource("03-scalar-in-exp-doc.yaml"); + AssertSequenceOfTokensFrom(scanner, + StreamStart, + DocumentStart, + SingleQuotedScalar("a scalar"), + DocumentEnd, + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample4() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("04-scalars-in-multi-docs.yaml"), + StreamStart, + SingleQuotedScalar("a scalar"), + DocumentStart, + SingleQuotedScalar("another scalar"), + DocumentStart, + SingleQuotedScalar("yet another scalar"), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample5() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("05-circular-sequence.yaml"), + StreamStart, + Anchor("A"), + FlowSequenceStart, + AnchorAlias("A"), + FlowSequenceEnd, + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample6() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("06-float-tag.yaml"), + StreamStart, + Tag("!!", "float"), + DoubleQuotedScalar("3.14"), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample7() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("07-scalar-styles.yaml"), + StreamStart, + DocumentStart, + DocumentStart, + PlainScalar("a plain scalar"), + DocumentStart, + SingleQuotedScalar("a single-quoted scalar"), + DocumentStart, + DoubleQuotedScalar("a double-quoted scalar"), + DocumentStart, + LiteralScalar("a literal scalar"), + DocumentStart, + FoldedScalar("a folded scalar"), + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample8() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("08-flow-sequence.yaml"), + StreamStart, + FlowSequenceStart, + PlainScalar("item 1"), + FlowEntry, + PlainScalar("item 2"), + FlowEntry, + PlainScalar("item 3"), + FlowSequenceEnd, + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample9() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("09-flow-mapping.yaml"), + StreamStart, + FlowMappingStart, + Key, + PlainScalar("a simple key"), + Value, + PlainScalar("a value"), + FlowEntry, + Key, + PlainScalar("a complex key"), + Value, + PlainScalar("another value"), + FlowEntry, + FlowMappingEnd, + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample10() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("10-mixed-nodes-in-sequence.yaml"), + StreamStart, + BlockSequenceStart, + BlockEntry, + PlainScalar("item 1"), + BlockEntry, + PlainScalar("item 2"), + BlockEntry, + BlockSequenceStart, + BlockEntry, + PlainScalar("item 3.1"), + BlockEntry, + PlainScalar("item 3.2"), + BlockEnd, + BlockEntry, + BlockMappingStart, + Key, + PlainScalar("key 1"), + Value, + PlainScalar("value 1"), + Key, + PlainScalar("key 2"), + Value, + PlainScalar("value 2"), + BlockEnd, + BlockEnd, + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample11() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("11-mixed-nodes-in-mapping.yaml"), + StreamStart, + BlockMappingStart, + Key, + PlainScalar("a simple key"), + Value, + PlainScalar("a value"), + Key, + PlainScalar("a complex key"), + Value, + PlainScalar("another value"), + Key, + PlainScalar("a mapping"), + Value, + BlockMappingStart, + Key, + PlainScalar("key 1"), + Value, + PlainScalar("value 1"), + Key, + PlainScalar("key 2"), + Value, + PlainScalar("value 2"), + BlockEnd, + Key, + PlainScalar("a sequence"), + Value, + BlockSequenceStart, + BlockEntry, + PlainScalar("item 1"), + BlockEntry, + PlainScalar("item 2"), + BlockEnd, + BlockEnd, + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample12() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("12-compact-sequence.yaml"), + StreamStart, + BlockSequenceStart, + BlockEntry, + BlockSequenceStart, + BlockEntry, + PlainScalar("item 1"), + BlockEntry, + PlainScalar("item 2"), + BlockEnd, + BlockEntry, + BlockMappingStart, + Key, + PlainScalar("key 1"), + Value, + PlainScalar("value 1"), + Key, + PlainScalar("key 2"), + Value, + PlainScalar("value 2"), + BlockEnd, + BlockEntry, + BlockMappingStart, + Key, + PlainScalar("complex key"), + Value, + PlainScalar("complex value"), + BlockEnd, + BlockEnd, + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample13() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("13-compact-mapping.yaml"), + StreamStart, + BlockMappingStart, + Key, + PlainScalar("a sequence"), + Value, + BlockSequenceStart, + BlockEntry, + PlainScalar("item 1"), + BlockEntry, + PlainScalar("item 2"), + BlockEnd, + Key, + PlainScalar("a mapping"), + Value, + BlockMappingStart, + Key, + PlainScalar("key 1"), + Value, + PlainScalar("value 1"), + Key, + PlainScalar("key 2"), + Value, + PlainScalar("value 2"), + BlockEnd, + BlockEnd, + StreamEnd); + } + + [Fact] + public void VerifyTokensOnExample14() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForResource("14-mapping-wo-indent.yaml"), + StreamStart, + BlockMappingStart, + Key, + PlainScalar("key"), + Value, + BlockEntry, + PlainScalar("item 1"), + BlockEntry, + PlainScalar("item 2"), + BlockEnd, + StreamEnd); + } + + [Fact] + public void CommentsAreReturnedWhenRequested() + { + AssertSequenceOfTokensFrom(new Scanner(Yaml.ReaderForText(@" + # Top comment + - first # Comment on first item + - second + # Bottom comment + "), skipComments: false), + StreamStart, + StandaloneComment("Top comment"), + BlockSequenceStart, + BlockEntry, + PlainScalar("first"), + InlineComment("Comment on first item"), + BlockEntry, + PlainScalar("second"), + StandaloneComment("Bottom comment"), + BlockEnd, + StreamEnd); + } + + [Fact] + public void CommentsAreCorrectlyMarked() + { + var sut = new Scanner(Yaml.ReaderForText(@" + - first # Comment on first item + "), skipComments: false); + + while (sut.MoveNext()) + { + if (sut.Current is Comment comment) + { + Assert.Equal(8, comment.Start.Index); + Assert.Equal(31, comment.End.Index); + + return; + } + } + + Assert.True(false, "Did not find a comment"); + } + + [Fact] + public void CommentsAreOmittedUnlessRequested() + { + AssertSequenceOfTokensFrom(Yaml.ScannerForText(@" + # Top comment + - first # Comment on first item + - second + # Bottom comment + "), + StreamStart, + BlockSequenceStart, + BlockEntry, + PlainScalar("first"), + BlockEntry, + PlainScalar("second"), + BlockEnd, + StreamEnd); + } + + [Fact] + public void MarksOnDoubleQuotedScalarsAreCorrect() + { + var scanner = Yaml.ScannerForText(@" + ""x"" + "); + + Scalar scalar = null; + while (scanner.MoveNext() && scalar == null) + { + scalar = scanner.Current as Scalar; + } + Assert.Equal(4, scalar.End.Column); + } + + [Fact] + public void Slow_stream_is_parsed_correctly() + { + var buffer = new MemoryStream(); + Yaml.StreamFrom("04-scalars-in-multi-docs.yaml").CopyTo(buffer); + + var slowStream = new SlowStream(buffer.ToArray()); + + var scanner = new Scanner(new StreamReader(slowStream)); + + scanner.MoveNext(); + + // Should not fail + scanner.MoveNext(); + } + + [Fact] + public void Issue_553_562() + { + var yaml = "MainItem4:\n" + string.Join("\n", Enumerable.Range(1, 100).Select(e => $"- {{item: {{foo1: {e}, foo2: 'bar{e}' }}}}")); + + var scanner = new Scanner(new StringReader(yaml)); + while (scanner.MoveNext()) + { + } + } + + private void AssertPartialSequenceOfTokensFrom(Scanner scanner, params Token[] tokens) + { + var tokenNumber = 1; + foreach (var expected in tokens) + { + scanner.MoveNext().Should().BeTrue("Missing token number {0}", tokenNumber); + AssertToken(expected, scanner.Current, tokenNumber); + tokenNumber++; + } + } + + private void AssertSequenceOfTokensFrom(Scanner scanner, params Token[] tokens) + { + AssertPartialSequenceOfTokensFrom(scanner, tokens); + scanner.MoveNext().Should().BeFalse("Found extra tokens"); + } + + private void AssertToken(Token expected, Token actual, int tokenNumber) + { + actual.Should().NotBeNull(); + actual.GetType().Should().Be(expected.GetType(), "Token {0} is not of the expected type", tokenNumber); + + foreach (var property in expected.GetType().GetTypeInfo().GetProperties()) + { + if (property.PropertyType != typeof(Mark) && property.CanRead && property.Name != "IsKey") + { + var value = property.GetValue(actual, null); + var expectedValue = property.GetValue(expected, null); + value.Should().Be(expectedValue, "Comparing property {0} in token {1}", property.Name, tokenNumber); + } + } + } + + /// + /// A stream that reads one byte at the time. + /// + public class SlowStream : Stream + { + private readonly byte[] data; + private int position; + + public SlowStream(byte[] data) + { + this.data = data; + } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => data.Length; + + public override long Position + { + get => position; + set => throw new NotSupportedException(); + } + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0 || position == data.Length) + { + return 0; + } + + buffer[offset] = data[position]; + ++position; + return 1; + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } + } +} diff --git a/YamlDotNet.Test/Core/TokenHelper.cs b/YamlDotNet.Test/Core/TokenHelper.cs index c13b37d5d..d04ed8e0c 100644 --- a/YamlDotNet.Test/Core/TokenHelper.cs +++ b/YamlDotNet.Test/Core/TokenHelper.cs @@ -1,165 +1,165 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core; -using YamlDotNet.Core.Tokens; - -namespace YamlDotNet.Test.Core -{ - public class TokenHelper - { - protected static StreamStart StreamStart - { - get { return new StreamStart(); } - } - - protected static StreamEnd StreamEnd - { - get { return new StreamEnd(); } - } - - protected static DocumentStart DocumentStart - { - get { return new DocumentStart(); } - } - - protected static DocumentEnd DocumentEnd - { - get { return new DocumentEnd(); } - } - - protected static VersionDirective VersionDirective(int major, int minor) - { - return new VersionDirective(new Version(major, minor)); - } - - protected static TagDirective TagDirective(string handle, string prefix) - { - return new TagDirective(handle, prefix); - } - - protected static Tag Tag(string handle, string suffix) - { - return new Tag(handle, suffix); - } - - protected static Scalar PlainScalar(string text) - { - return new Scalar(text, ScalarStyle.Plain); - } - - protected static Scalar SingleQuotedScalar(string text) - { - return new Scalar(text, ScalarStyle.SingleQuoted); - } - - protected static Scalar DoubleQuotedScalar(string text) - { - return new Scalar(text, ScalarStyle.DoubleQuoted); - } - - protected static Scalar LiteralScalar(string text) - { - return new Scalar(text, ScalarStyle.Literal); - } - - protected static Scalar FoldedScalar(string text) - { - return new Scalar(text, ScalarStyle.Folded); - } - - protected static FlowSequenceStart FlowSequenceStart - { - get { return new FlowSequenceStart(); } - } - - protected static FlowSequenceEnd FlowSequenceEnd - { - get { return new FlowSequenceEnd(); } - } - - protected static BlockSequenceStart BlockSequenceStart - { - get { return new BlockSequenceStart(); } - } - - protected static FlowMappingStart FlowMappingStart - { - get { return new FlowMappingStart(); } - } - - protected static FlowMappingEnd FlowMappingEnd - { - get { return new FlowMappingEnd(); } - } - - protected static BlockMappingStart BlockMappingStart - { - get { return new BlockMappingStart(); } - } - - protected static Key Key - { - get { return new Key(); } - } - - protected static Value Value - { - get { return new Value(); } - } - - protected static FlowEntry FlowEntry - { - get { return new FlowEntry(); } - } - - protected static BlockEntry BlockEntry - { - get { return new BlockEntry(); } - } - - protected static BlockEnd BlockEnd - { - get { return new BlockEnd(); } - } - - protected static Anchor Anchor(string anchor) - { - return new Anchor(new AnchorName(anchor)); - } - - protected static AnchorAlias AnchorAlias(string alias) - { - return new AnchorAlias(new AnchorName(alias)); - } - - protected static Comment StandaloneComment(string text) - { - return new Comment(text, false); - } - - - protected static Comment InlineComment(string text) - { - return new Comment(text, true); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core; +using YamlDotNet.Core.Tokens; + +namespace YamlDotNet.Test.Core +{ + public class TokenHelper + { + protected static StreamStart StreamStart + { + get { return new StreamStart(); } + } + + protected static StreamEnd StreamEnd + { + get { return new StreamEnd(); } + } + + protected static DocumentStart DocumentStart + { + get { return new DocumentStart(); } + } + + protected static DocumentEnd DocumentEnd + { + get { return new DocumentEnd(); } + } + + protected static VersionDirective VersionDirective(int major, int minor) + { + return new VersionDirective(new Version(major, minor)); + } + + protected static TagDirective TagDirective(string handle, string prefix) + { + return new TagDirective(handle, prefix); + } + + protected static Tag Tag(string handle, string suffix) + { + return new Tag(handle, suffix); + } + + protected static Scalar PlainScalar(string text) + { + return new Scalar(text, ScalarStyle.Plain); + } + + protected static Scalar SingleQuotedScalar(string text) + { + return new Scalar(text, ScalarStyle.SingleQuoted); + } + + protected static Scalar DoubleQuotedScalar(string text) + { + return new Scalar(text, ScalarStyle.DoubleQuoted); + } + + protected static Scalar LiteralScalar(string text) + { + return new Scalar(text, ScalarStyle.Literal); + } + + protected static Scalar FoldedScalar(string text) + { + return new Scalar(text, ScalarStyle.Folded); + } + + protected static FlowSequenceStart FlowSequenceStart + { + get { return new FlowSequenceStart(); } + } + + protected static FlowSequenceEnd FlowSequenceEnd + { + get { return new FlowSequenceEnd(); } + } + + protected static BlockSequenceStart BlockSequenceStart + { + get { return new BlockSequenceStart(); } + } + + protected static FlowMappingStart FlowMappingStart + { + get { return new FlowMappingStart(); } + } + + protected static FlowMappingEnd FlowMappingEnd + { + get { return new FlowMappingEnd(); } + } + + protected static BlockMappingStart BlockMappingStart + { + get { return new BlockMappingStart(); } + } + + protected static Key Key + { + get { return new Key(); } + } + + protected static Value Value + { + get { return new Value(); } + } + + protected static FlowEntry FlowEntry + { + get { return new FlowEntry(); } + } + + protected static BlockEntry BlockEntry + { + get { return new BlockEntry(); } + } + + protected static BlockEnd BlockEnd + { + get { return new BlockEnd(); } + } + + protected static Anchor Anchor(string anchor) + { + return new Anchor(new AnchorName(anchor)); + } + + protected static AnchorAlias AnchorAlias(string alias) + { + return new AnchorAlias(new AnchorName(alias)); + } + + protected static Comment StandaloneComment(string text) + { + return new Comment(text, false); + } + + + protected static Comment InlineComment(string text) + { + return new Comment(text, true); + } + } +} diff --git a/YamlDotNet.Test/Core/YamlExceptionTests.cs b/YamlDotNet.Test/Core/YamlExceptionTests.cs index c955ff922..2c3e66f25 100644 --- a/YamlDotNet.Test/Core/YamlExceptionTests.cs +++ b/YamlDotNet.Test/Core/YamlExceptionTests.cs @@ -1,63 +1,63 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using FluentAssertions; -using Xunit; -using YamlDotNet.Core; - -namespace YamlDotNet.Test.Core -{ - public class YamlExceptionTests - { - [Fact] - public void VerifyToStringWithEmptyMarks() - { - var exception = new YamlException(Mark.Empty, Mark.Empty, "Test exception message"); - exception.ToString().Should().Be("(Line: 1, Col: 1, Idx: 0) - (Line: 1, Col: 1, Idx: 0): Test exception message"); - exception.Message.Should().Be("Test exception message"); - } - - [Fact] - public void VerifyToStringWithNonEmptyMarks() - { - var exception = new YamlException(new Mark(1, 1, 1), new Mark(10, 10, 10), "Test exception message"); - exception.ToString().Should().Be("(Line: 1, Col: 1, Idx: 1) - (Line: 10, Col: 10, Idx: 10): Test exception message"); - exception.Message.Should().Be("Test exception message"); - } - - [Fact] - public void VerifyToStringWithInnerExceptionAndMarks() - { - var exception = new YamlException(new Mark(1, 1, 1), new Mark(10, 10, 10), "Test exception message", new InvalidOperationException("Test inner exception")); - exception.ToString().Should().Be("(Line: 1, Col: 1, Idx: 1) - (Line: 10, Col: 10, Idx: 10): Test exception message"); - exception.Message.Should().Be("Test exception message"); - } - - [Fact] - public void VerifyToStringWithInnerException() - { - var exception = new YamlException("Test exception message", new InvalidOperationException("Test inner exception")); - exception.ToString().Should().Be("(Line: 1, Col: 1, Idx: 0) - (Line: 1, Col: 1, Idx: 0): Test exception message"); - exception.Message.Should().Be("Test exception message"); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using FluentAssertions; +using Xunit; +using YamlDotNet.Core; + +namespace YamlDotNet.Test.Core +{ + public class YamlExceptionTests + { + [Fact] + public void VerifyToStringWithEmptyMarks() + { + var exception = new YamlException(Mark.Empty, Mark.Empty, "Test exception message"); + exception.ToString().Should().Be("(Line: 1, Col: 1, Idx: 0) - (Line: 1, Col: 1, Idx: 0): Test exception message"); + exception.Message.Should().Be("Test exception message"); + } + + [Fact] + public void VerifyToStringWithNonEmptyMarks() + { + var exception = new YamlException(new Mark(1, 1, 1), new Mark(10, 10, 10), "Test exception message"); + exception.ToString().Should().Be("(Line: 1, Col: 1, Idx: 1) - (Line: 10, Col: 10, Idx: 10): Test exception message"); + exception.Message.Should().Be("Test exception message"); + } + + [Fact] + public void VerifyToStringWithInnerExceptionAndMarks() + { + var exception = new YamlException(new Mark(1, 1, 1), new Mark(10, 10, 10), "Test exception message", new InvalidOperationException("Test inner exception")); + exception.ToString().Should().Be("(Line: 1, Col: 1, Idx: 1) - (Line: 10, Col: 10, Idx: 10): Test exception message"); + exception.Message.Should().Be("Test exception message"); + } + + [Fact] + public void VerifyToStringWithInnerException() + { + var exception = new YamlException("Test exception message", new InvalidOperationException("Test inner exception")); + exception.ToString().Should().Be("(Line: 1, Col: 1, Idx: 0) - (Line: 1, Col: 1, Idx: 0): Test exception message"); + exception.Message.Should().Be("Test exception message"); + } + } +} diff --git a/YamlDotNet.Test/EnumerableExtensions.cs b/YamlDotNet.Test/EnumerableExtensions.cs index 5dc41f23f..5a89b6c1f 100644 --- a/YamlDotNet.Test/EnumerableExtensions.cs +++ b/YamlDotNet.Test/EnumerableExtensions.cs @@ -1,46 +1,46 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; - -namespace YamlDotNet.Test -{ - public static class EnumerableExtensions - { - public static IEnumerable Do(this IEnumerable source, Action action) - { - foreach (var item in source) - { - action(item); - yield return item; - } - } - - public static void Run(this IEnumerable source, Action action) - { - foreach (var element in source) - { - action(element); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace YamlDotNet.Test +{ + public static class EnumerableExtensions + { + public static IEnumerable Do(this IEnumerable source, Action action) + { + foreach (var item in source) + { + action(item); + yield return item; + } + } + + public static void Run(this IEnumerable source, Action action) + { + foreach (var element in source) + { + action(element); + } + } + } +} diff --git a/YamlDotNet.Test/GlobalSuppressions.cs b/YamlDotNet.Test/GlobalSuppressions.cs index 3a77adb25..bafe7303e 100644 --- a/YamlDotNet.Test/GlobalSuppressions.cs +++ b/YamlDotNet.Test/GlobalSuppressions.cs @@ -1,29 +1,29 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Many test classes use non-compliant properties to test specific cases.", Scope = "namespaceanddescendants", Target = "~N:YamlDotNet.Test")] +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Many test classes use non-compliant properties to test specific cases.", Scope = "namespaceanddescendants", Target = "~N:YamlDotNet.Test")] diff --git a/YamlDotNet.Test/Helpers/OrderedDictionaryTests.cs b/YamlDotNet.Test/Helpers/OrderedDictionaryTests.cs index a2e577e59..4951295a6 100644 --- a/YamlDotNet.Test/Helpers/OrderedDictionaryTests.cs +++ b/YamlDotNet.Test/Helpers/OrderedDictionaryTests.cs @@ -1,109 +1,109 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using System.Linq; -using Xunit; -using YamlDotNet.Helpers; - -namespace YamlDotNet.Test.Helpers -{ - public class OrderedDictionaryTests - { - [Fact] - public void OrderOfElementsIsMainted() - { - var dict = (IDictionary)new OrderedDictionary - { - { 3, "First" }, - { 2, "Temporary" }, - { 1, "Second" }, - }; - dict.Remove(2); - dict.Add(4, "Inserted"); - dict[4] = "Third"; - - Assert.Equal(3, dict.Count); - Assert.Equal(new KeyValuePair(3, "First"), dict.First()); - Assert.Equal(new KeyValuePair(1, "Second"), dict.Skip(1).First()); - Assert.Equal(new KeyValuePair(4, "Third"), dict.Skip(2).First()); - Assert.Equal(new[] { 3, 1, 4 }, dict.Keys.ToArray()); - Assert.Equal(new[] { "First", "Second", "Third" }, dict.Values.ToArray()); - } - - [Fact] - public void KeysContainsWorks() - { - var dict = new OrderedDictionary - { - { 3, "First item" }, - { 2, "Second item" }, - { 1, "Third item" }, - }; - - Assert.False(dict.Keys.Contains(0)); - Assert.True(dict.Keys.Contains(1)); - Assert.True(dict.Keys.Contains(2)); - Assert.True(dict.Keys.Contains(3)); - Assert.False(dict.Keys.Contains(4)); - } - - [Fact] - public void ValuesContainsWorks() - { - var dict = new OrderedDictionary - { - { 3, "First item" }, - { 2, "Second item" }, - { 1, "Third item" }, - }; - - Assert.False(dict.Values.Contains(null)); - Assert.True(dict.Values.Contains("First item")); - Assert.True(dict.Values.Contains("Second item")); - Assert.True(dict.Values.Contains("Third item")); - Assert.False(dict.Values.Contains("Fourth item")); - } - - [Fact] - public void CanInsertAndRemoveAtIndex() - { - var dict = new OrderedDictionary - { - { 3, "First" }, - { 2, "Temporary" }, - { 1, "Second" }, - }; - dict.RemoveAt(1); - dict.Insert(0, 4, "Zero"); - - Assert.Equal(3, dict.Count); - Assert.Equal(new KeyValuePair(4, "Zero"), dict.First()); - Assert.Equal(new KeyValuePair(3, "First"), dict.Skip(1).First()); - Assert.Equal(new KeyValuePair(1, "Second"), dict.Skip(2).First()); - Assert.Equal(new KeyValuePair(4, "Zero"), dict[0]); - Assert.Equal(new KeyValuePair(3, "First"), dict[1]); - Assert.Equal(new KeyValuePair(1, "Second"), dict[2]); - Assert.Equal(new[] { 4, 3, 1 }, dict.Keys.ToArray()); - Assert.Equal(new[] { "Zero", "First", "Second" }, dict.Values.ToArray()); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using System.Linq; +using Xunit; +using YamlDotNet.Helpers; + +namespace YamlDotNet.Test.Helpers +{ + public class OrderedDictionaryTests + { + [Fact] + public void OrderOfElementsIsMainted() + { + var dict = (IDictionary)new OrderedDictionary + { + { 3, "First" }, + { 2, "Temporary" }, + { 1, "Second" }, + }; + dict.Remove(2); + dict.Add(4, "Inserted"); + dict[4] = "Third"; + + Assert.Equal(3, dict.Count); + Assert.Equal(new KeyValuePair(3, "First"), dict.First()); + Assert.Equal(new KeyValuePair(1, "Second"), dict.Skip(1).First()); + Assert.Equal(new KeyValuePair(4, "Third"), dict.Skip(2).First()); + Assert.Equal(new[] { 3, 1, 4 }, dict.Keys.ToArray()); + Assert.Equal(new[] { "First", "Second", "Third" }, dict.Values.ToArray()); + } + + [Fact] + public void KeysContainsWorks() + { + var dict = new OrderedDictionary + { + { 3, "First item" }, + { 2, "Second item" }, + { 1, "Third item" }, + }; + + Assert.False(dict.Keys.Contains(0)); + Assert.True(dict.Keys.Contains(1)); + Assert.True(dict.Keys.Contains(2)); + Assert.True(dict.Keys.Contains(3)); + Assert.False(dict.Keys.Contains(4)); + } + + [Fact] + public void ValuesContainsWorks() + { + var dict = new OrderedDictionary + { + { 3, "First item" }, + { 2, "Second item" }, + { 1, "Third item" }, + }; + + Assert.False(dict.Values.Contains(null)); + Assert.True(dict.Values.Contains("First item")); + Assert.True(dict.Values.Contains("Second item")); + Assert.True(dict.Values.Contains("Third item")); + Assert.False(dict.Values.Contains("Fourth item")); + } + + [Fact] + public void CanInsertAndRemoveAtIndex() + { + var dict = new OrderedDictionary + { + { 3, "First" }, + { 2, "Temporary" }, + { 1, "Second" }, + }; + dict.RemoveAt(1); + dict.Insert(0, 4, "Zero"); + + Assert.Equal(3, dict.Count); + Assert.Equal(new KeyValuePair(4, "Zero"), dict.First()); + Assert.Equal(new KeyValuePair(3, "First"), dict.Skip(1).First()); + Assert.Equal(new KeyValuePair(1, "Second"), dict.Skip(2).First()); + Assert.Equal(new KeyValuePair(4, "Zero"), dict[0]); + Assert.Equal(new KeyValuePair(3, "First"), dict[1]); + Assert.Equal(new KeyValuePair(1, "Second"), dict[2]); + Assert.Equal(new[] { 4, 3, 1 }, dict.Keys.ToArray()); + Assert.Equal(new[] { "Zero", "First", "Second" }, dict.Values.ToArray()); + } + } +} diff --git a/YamlDotNet.Test/Helpers/PortabilityTests.cs b/YamlDotNet.Test/Helpers/PortabilityTests.cs index 0d7f424d6..4de3fe7bc 100644 --- a/YamlDotNet.Test/Helpers/PortabilityTests.cs +++ b/YamlDotNet.Test/Helpers/PortabilityTests.cs @@ -1,43 +1,43 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Globalization; -using Xunit; - -namespace YamlDotNet.Test.Helpers -{ - public class PortabilityTests - { - [Fact] - public void GetPublicStaticMethodReturnsCorrectMethodInfo() - { - var expected = DateTimeOffset.UtcNow; - - var type = typeof(DateTimeOffset); - var method = type.GetPublicStaticMethod("Parse", typeof(string), typeof(IFormatProvider)); - - var actual = (DateTimeOffset)method.Invoke(null, new object[] { expected.ToString("o"), CultureInfo.InvariantCulture }); - - Assert.Equal(expected, actual); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Globalization; +using Xunit; + +namespace YamlDotNet.Test.Helpers +{ + public class PortabilityTests + { + [Fact] + public void GetPublicStaticMethodReturnsCorrectMethodInfo() + { + var expected = DateTimeOffset.UtcNow; + + var type = typeof(DateTimeOffset); + var method = type.GetPublicStaticMethod("Parse", typeof(string), typeof(IFormatProvider)); + + var actual = (DateTimeOffset)method.Invoke(null, new object[] { expected.ToString("o"), CultureInfo.InvariantCulture }); + + Assert.Equal(expected, actual); + } + } +} diff --git a/YamlDotNet.Test/RepresentationModel/YamlStreamTests.cs b/YamlDotNet.Test/RepresentationModel/YamlStreamTests.cs index 8dc96b3dd..d06ec33c6 100644 --- a/YamlDotNet.Test/RepresentationModel/YamlStreamTests.cs +++ b/YamlDotNet.Test/RepresentationModel/YamlStreamTests.cs @@ -1,317 +1,317 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using FluentAssertions; -using Xunit; -using YamlDotNet.Core; -using YamlDotNet.RepresentationModel; - -namespace YamlDotNet.Test.RepresentationModel -{ - public class YamlStreamTests - { - [Fact] - public void LoadSimpleDocument() - { - var stream = new YamlStream(); - stream.Load(Yaml.ReaderFrom("02-scalar-in-imp-doc.yaml")); - - Assert.Equal(1, stream.Documents.Count); - Assert.IsType(stream.Documents[0].RootNode); - Assert.Equal("a scalar", ((YamlScalarNode)stream.Documents[0].RootNode).Value); - Assert.Equal(YamlNodeType.Scalar, stream.Documents[0].RootNode.NodeType); - } - - [Fact] - public void AccessingAllNodesOnInfinitelyRecursiveDocumentThrows() - { - var stream = new YamlStream(); - stream.Load(Yaml.ParserForText("&a [*a]")); - - var accessAllNodes = new Action(() => stream.Documents.Single().AllNodes.ToList()); - - accessAllNodes.ShouldThrow("because the document is infinitely recursive."); - } - - [Fact] - public void InfinitelyRecursiveNodeToStringSucceeds() - { - var stream = new YamlStream(); - stream.Load(Yaml.ParserForText("&a [*a]")); - - var toString = stream.Documents.Single().RootNode.ToString(); - - toString.Should().Contain("WARNING! INFINITE RECURSION!"); - } - - [Fact] - public void BackwardAliasReferenceWorks() - { - var stream = new YamlStream(); - stream.Load(Yaml.ReaderFrom("backwards-alias.yaml")); - - Assert.Equal(1, stream.Documents.Count); - Assert.IsType(stream.Documents[0].RootNode); - - var sequence = (YamlSequenceNode)stream.Documents[0].RootNode; - Assert.Equal(3, sequence.Children.Count); - - Assert.Equal("a scalar", ((YamlScalarNode)sequence.Children[0]).Value); - Assert.Equal("another scalar", ((YamlScalarNode)sequence.Children[1]).Value); - Assert.Equal("a scalar", ((YamlScalarNode)sequence.Children[2]).Value); - Assert.Same(sequence.Children[0], sequence.Children[2]); - } - - [Fact] - public void ForwardAliasReferenceWorks() - { - var stream = new YamlStream(); - stream.Load(Yaml.ReaderFrom("forward-alias.yaml")); - - Assert.Equal(1, stream.Documents.Count); - Assert.IsType(stream.Documents[0].RootNode); - - var sequence = (YamlSequenceNode)stream.Documents[0].RootNode; - Assert.Equal(3, sequence.Children.Count); - - Assert.Equal("a scalar", ((YamlScalarNode)sequence.Children[0]).Value); - Assert.Equal("another scalar", ((YamlScalarNode)sequence.Children[1]).Value); - Assert.Equal("a scalar", ((YamlScalarNode)sequence.Children[2]).Value); - Assert.Same(sequence.Children[0], sequence.Children[2]); - } - - [Fact] - public void RoundtripExample1() - { - RoundtripTest("01-directives.yaml"); - } - - [Fact] - public void RoundtripExample2() - { - RoundtripTest("02-scalar-in-imp-doc.yaml"); - } - - [Fact] - public void RoundtripExample3() - { - RoundtripTest("03-scalar-in-exp-doc.yaml"); - } - - [Fact] - public void RoundtripExample4() - { - RoundtripTest("04-scalars-in-multi-docs.yaml"); - } - - [Fact] - public void RoundtripExample5() - { - RoundtripTest("06-float-tag.yaml"); - } - - [Fact] - public void RoundtripExample6() - { - RoundtripTest("06-float-tag.yaml"); - } - - [Fact] - public void RoundtripExample7() - { - RoundtripTest("07-scalar-styles.yaml"); - } - - [Fact] - public void RoundtripExample8() - { - RoundtripTest("08-flow-sequence.yaml"); - } - - [Fact] - public void RoundtripExample9() - { - RoundtripTest("09-flow-mapping.yaml"); - } - - [Fact] - public void RoundtripExample10() - { - RoundtripTest("10-mixed-nodes-in-sequence.yaml"); - } - - [Fact] - public void RoundtripExample11() - { - RoundtripTest("11-mixed-nodes-in-mapping.yaml"); - } - - [Fact] - public void RoundtripExample12() - { - RoundtripTest("12-compact-sequence.yaml"); - } - - [Fact] - public void RoundtripExample13() - { - RoundtripTest("13-compact-mapping.yaml"); - } - - [Fact] - public void RoundtripExample14() - { - RoundtripTest("14-mapping-wo-indent.yaml"); - } - - [Fact] - public void RoundtripBackreference() - { - RoundtripTest("backreference.yaml"); - } - - [Fact] - public void FailBackreference() - { - RoundtripTest("fail-backreference.yaml"); - } - - [Fact] - public void Roundtrip32BitsUnicodeEscape() - { - RoundtripTest("unicode-32bits-escape.yaml"); - } - - [Fact] - public void AnchorsOverwriting() - { - RoundtripTest("anchors-overwriting.yaml"); - } - - [Fact] - public void AllAliasesMustBeResolved() - { - var original = new YamlStream(); - Assert.Throws(() => original.Load(Yaml.ReaderFrom("invalid-reference.yaml"))); - } - - [Fact] - public void CanReadValueWithQuotes() - { - var yamlStream = new YamlStream(); - yamlStream.Load(Yaml.ParserForText("description: -\"\" should be zero")); - var nodes = yamlStream.Documents.Single().AllNodes.ToArray(); - Assert.Equal("description", nodes[1].ToString()); - Assert.Equal("-\"\" should be zero", nodes[2].ToString()); - } - - private void RoundtripTest(string yamlFileName) - { - var original = new YamlStream(); - original.Load(Yaml.ReaderFrom(yamlFileName)); - - var buffer = new StringBuilder(); - original.Save(new StringWriter(buffer)); - - var final = new YamlStream(); - final.Load(new StringReader(buffer.ToString())); - - var originalBuilder = new YamlDocumentStructureBuilder(); - original.Accept(originalBuilder); - - var finalBuilder = new YamlDocumentStructureBuilder(); - final.Accept(finalBuilder); - - Assert.Equal(originalBuilder.Events.Count, finalBuilder.Events.Count); - - for (var i = 0; i < originalBuilder.Events.Count; ++i) - { - var originalEvent = originalBuilder.Events[i]; - var finalEvent = finalBuilder.Events[i]; - - Assert.Equal(originalEvent.Type, finalEvent.Type); - Assert.Equal(originalEvent.Value, finalEvent.Value); - } - } - - private class YamlDocumentStructureBuilder : YamlVisitorBase - { - private readonly List events = new List(); - - public IList Events - { - get - { - return events; - } - } - - public override void Visit(YamlScalarNode scalar) - { - events.Add(new YamlNodeEvent(YamlNodeEventType.Scalar, scalar.Anchor, scalar.Tag, scalar.Value)); - } - - public override void Visit(YamlSequenceNode sequence) - { - events.Add(new YamlNodeEvent(YamlNodeEventType.SequenceStart, sequence.Anchor, sequence.Tag, null)); - base.Visit(sequence); - events.Add(new YamlNodeEvent(YamlNodeEventType.SequenceEnd, sequence.Anchor, sequence.Tag, null)); - } - - public override void Visit(YamlMappingNode mapping) - { - events.Add(new YamlNodeEvent(YamlNodeEventType.MappingStart, mapping.Anchor, mapping.Tag, null)); - base.Visit(mapping); - events.Add(new YamlNodeEvent(YamlNodeEventType.MappingEnd, mapping.Anchor, mapping.Tag, null)); - } - } - - private class YamlNodeEvent - { - public YamlNodeEventType Type { get; } - public AnchorName Anchor { get; } - public TagName Tag { get; } - public string Value { get; } - - public YamlNodeEvent(YamlNodeEventType type, AnchorName anchor, TagName tag, string value) - { - Type = type; - Anchor = anchor; - Tag = tag; - Value = value; - } - } - - private enum YamlNodeEventType - { - SequenceStart, - SequenceEnd, - MappingStart, - MappingEnd, - Scalar, - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using FluentAssertions; +using Xunit; +using YamlDotNet.Core; +using YamlDotNet.RepresentationModel; + +namespace YamlDotNet.Test.RepresentationModel +{ + public class YamlStreamTests + { + [Fact] + public void LoadSimpleDocument() + { + var stream = new YamlStream(); + stream.Load(Yaml.ReaderFrom("02-scalar-in-imp-doc.yaml")); + + Assert.Equal(1, stream.Documents.Count); + Assert.IsType(stream.Documents[0].RootNode); + Assert.Equal("a scalar", ((YamlScalarNode)stream.Documents[0].RootNode).Value); + Assert.Equal(YamlNodeType.Scalar, stream.Documents[0].RootNode.NodeType); + } + + [Fact] + public void AccessingAllNodesOnInfinitelyRecursiveDocumentThrows() + { + var stream = new YamlStream(); + stream.Load(Yaml.ParserForText("&a [*a]")); + + var accessAllNodes = new Action(() => stream.Documents.Single().AllNodes.ToList()); + + accessAllNodes.ShouldThrow("because the document is infinitely recursive."); + } + + [Fact] + public void InfinitelyRecursiveNodeToStringSucceeds() + { + var stream = new YamlStream(); + stream.Load(Yaml.ParserForText("&a [*a]")); + + var toString = stream.Documents.Single().RootNode.ToString(); + + toString.Should().Contain("WARNING! INFINITE RECURSION!"); + } + + [Fact] + public void BackwardAliasReferenceWorks() + { + var stream = new YamlStream(); + stream.Load(Yaml.ReaderFrom("backwards-alias.yaml")); + + Assert.Equal(1, stream.Documents.Count); + Assert.IsType(stream.Documents[0].RootNode); + + var sequence = (YamlSequenceNode)stream.Documents[0].RootNode; + Assert.Equal(3, sequence.Children.Count); + + Assert.Equal("a scalar", ((YamlScalarNode)sequence.Children[0]).Value); + Assert.Equal("another scalar", ((YamlScalarNode)sequence.Children[1]).Value); + Assert.Equal("a scalar", ((YamlScalarNode)sequence.Children[2]).Value); + Assert.Same(sequence.Children[0], sequence.Children[2]); + } + + [Fact] + public void ForwardAliasReferenceWorks() + { + var stream = new YamlStream(); + stream.Load(Yaml.ReaderFrom("forward-alias.yaml")); + + Assert.Equal(1, stream.Documents.Count); + Assert.IsType(stream.Documents[0].RootNode); + + var sequence = (YamlSequenceNode)stream.Documents[0].RootNode; + Assert.Equal(3, sequence.Children.Count); + + Assert.Equal("a scalar", ((YamlScalarNode)sequence.Children[0]).Value); + Assert.Equal("another scalar", ((YamlScalarNode)sequence.Children[1]).Value); + Assert.Equal("a scalar", ((YamlScalarNode)sequence.Children[2]).Value); + Assert.Same(sequence.Children[0], sequence.Children[2]); + } + + [Fact] + public void RoundtripExample1() + { + RoundtripTest("01-directives.yaml"); + } + + [Fact] + public void RoundtripExample2() + { + RoundtripTest("02-scalar-in-imp-doc.yaml"); + } + + [Fact] + public void RoundtripExample3() + { + RoundtripTest("03-scalar-in-exp-doc.yaml"); + } + + [Fact] + public void RoundtripExample4() + { + RoundtripTest("04-scalars-in-multi-docs.yaml"); + } + + [Fact] + public void RoundtripExample5() + { + RoundtripTest("06-float-tag.yaml"); + } + + [Fact] + public void RoundtripExample6() + { + RoundtripTest("06-float-tag.yaml"); + } + + [Fact] + public void RoundtripExample7() + { + RoundtripTest("07-scalar-styles.yaml"); + } + + [Fact] + public void RoundtripExample8() + { + RoundtripTest("08-flow-sequence.yaml"); + } + + [Fact] + public void RoundtripExample9() + { + RoundtripTest("09-flow-mapping.yaml"); + } + + [Fact] + public void RoundtripExample10() + { + RoundtripTest("10-mixed-nodes-in-sequence.yaml"); + } + + [Fact] + public void RoundtripExample11() + { + RoundtripTest("11-mixed-nodes-in-mapping.yaml"); + } + + [Fact] + public void RoundtripExample12() + { + RoundtripTest("12-compact-sequence.yaml"); + } + + [Fact] + public void RoundtripExample13() + { + RoundtripTest("13-compact-mapping.yaml"); + } + + [Fact] + public void RoundtripExample14() + { + RoundtripTest("14-mapping-wo-indent.yaml"); + } + + [Fact] + public void RoundtripBackreference() + { + RoundtripTest("backreference.yaml"); + } + + [Fact] + public void FailBackreference() + { + RoundtripTest("fail-backreference.yaml"); + } + + [Fact] + public void Roundtrip32BitsUnicodeEscape() + { + RoundtripTest("unicode-32bits-escape.yaml"); + } + + [Fact] + public void AnchorsOverwriting() + { + RoundtripTest("anchors-overwriting.yaml"); + } + + [Fact] + public void AllAliasesMustBeResolved() + { + var original = new YamlStream(); + Assert.Throws(() => original.Load(Yaml.ReaderFrom("invalid-reference.yaml"))); + } + + [Fact] + public void CanReadValueWithQuotes() + { + var yamlStream = new YamlStream(); + yamlStream.Load(Yaml.ParserForText("description: -\"\" should be zero")); + var nodes = yamlStream.Documents.Single().AllNodes.ToArray(); + Assert.Equal("description", nodes[1].ToString()); + Assert.Equal("-\"\" should be zero", nodes[2].ToString()); + } + + private void RoundtripTest(string yamlFileName) + { + var original = new YamlStream(); + original.Load(Yaml.ReaderFrom(yamlFileName)); + + var buffer = new StringBuilder(); + original.Save(new StringWriter(buffer)); + + var final = new YamlStream(); + final.Load(new StringReader(buffer.ToString())); + + var originalBuilder = new YamlDocumentStructureBuilder(); + original.Accept(originalBuilder); + + var finalBuilder = new YamlDocumentStructureBuilder(); + final.Accept(finalBuilder); + + Assert.Equal(originalBuilder.Events.Count, finalBuilder.Events.Count); + + for (var i = 0; i < originalBuilder.Events.Count; ++i) + { + var originalEvent = originalBuilder.Events[i]; + var finalEvent = finalBuilder.Events[i]; + + Assert.Equal(originalEvent.Type, finalEvent.Type); + Assert.Equal(originalEvent.Value, finalEvent.Value); + } + } + + private class YamlDocumentStructureBuilder : YamlVisitorBase + { + private readonly List events = new List(); + + public IList Events + { + get + { + return events; + } + } + + public override void Visit(YamlScalarNode scalar) + { + events.Add(new YamlNodeEvent(YamlNodeEventType.Scalar, scalar.Anchor, scalar.Tag, scalar.Value)); + } + + public override void Visit(YamlSequenceNode sequence) + { + events.Add(new YamlNodeEvent(YamlNodeEventType.SequenceStart, sequence.Anchor, sequence.Tag, null)); + base.Visit(sequence); + events.Add(new YamlNodeEvent(YamlNodeEventType.SequenceEnd, sequence.Anchor, sequence.Tag, null)); + } + + public override void Visit(YamlMappingNode mapping) + { + events.Add(new YamlNodeEvent(YamlNodeEventType.MappingStart, mapping.Anchor, mapping.Tag, null)); + base.Visit(mapping); + events.Add(new YamlNodeEvent(YamlNodeEventType.MappingEnd, mapping.Anchor, mapping.Tag, null)); + } + } + + private class YamlNodeEvent + { + public YamlNodeEventType Type { get; } + public AnchorName Anchor { get; } + public TagName Tag { get; } + public string Value { get; } + + public YamlNodeEvent(YamlNodeEventType type, AnchorName anchor, TagName tag, string value) + { + Type = type; + Anchor = anchor; + Tag = tag; + Value = value; + } + } + + private enum YamlNodeEventType + { + SequenceStart, + SequenceEnd, + MappingStart, + MappingEnd, + Scalar, + } + } +} diff --git a/YamlDotNet.Test/Serialization/DateTimeConverterTests.cs b/YamlDotNet.Test/Serialization/DateTimeConverterTests.cs index e92961e1a..8af6d86bd 100644 --- a/YamlDotNet.Test/Serialization/DateTimeConverterTests.cs +++ b/YamlDotNet.Test/Serialization/DateTimeConverterTests.cs @@ -1,579 +1,579 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Globalization; -using FakeItEasy; -using FluentAssertions; -using Xunit; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.Converters; -using YamlDotNet.Serialization.NamingConventions; - -namespace YamlDotNet.Test.Serialization -{ - /// - /// This represents the test entity for the class. - /// - public class DateTimeConverterTests - { - /// - /// Tests whether the Accepts() method should return expected result or not. - /// - /// to check. - /// Expected result. - [Theory] - [InlineData(typeof(DateTime), true)] - [InlineData(typeof(string), false)] - public void Given_Type_Accepts_ShouldReturn_Result(Type type, bool expected) - { - var converter = new DateTimeConverter(); - - var result = converter.Accepts(type); - - result.Should().Be(expected); - } - - /// - /// Tests whether the ReadYaml() method should throw or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// The converter instance uses its default parameters of "G" and UTC. - [Theory] - [InlineData(2016, 12, 31)] - public void Given_Yaml_WithInvalidDateTimeFormat_WithDefaultParameters_ReadYaml_ShouldThrow_Exception(int year, int month, int day) - { - var yaml = $"{year}-{month:00}-{day:00}"; - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); - - var converter = new DateTimeConverter(); - - Action action = () => { converter.ReadYaml(parser, typeof(DateTime)); }; - - action.ShouldThrow(); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Hour value. - /// Minute value. - /// Second value. - /// The converter instance uses its default parameters of "G" and UTC. - [Theory] - [InlineData(2016, 12, 31, 3, 0, 0)] - public void Given_Yaml_WithValidDateTimeFormat_WithDefaultParameters_ReadYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second) - { - var yaml = $"{month:00}/{day:00}/{year} {hour:00}:{minute:00}:{second:00}"; // This is the DateTime format of "G" - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); - - var converter = new DateTimeConverter(); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().BeOfType(); - ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); - ((DateTime)result).ToUniversalTime().Year.Should().Be(year); - ((DateTime)result).ToUniversalTime().Month.Should().Be(month); - ((DateTime)result).ToUniversalTime().Day.Should().Be(day); - ((DateTime)result).ToUniversalTime().Hour.Should().Be(hour); - ((DateTime)result).ToUniversalTime().Minute.Should().Be(minute); - ((DateTime)result).ToUniversalTime().Second.Should().Be(second); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Hour value. - /// Minute value. - /// Second value. - /// The converter instance uses its default parameters of "G". - [Theory] - [InlineData(2016, 12, 31, 3, 0, 0)] - public void Given_Yaml_WithValidDateTimeFormat_WithDefaultParameterAndUnspecified_ReadYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second) - { - var yaml = $"{month:00}/{day:00}/{year} {hour:00}:{minute:00}:{second:00}"; // This is the DateTime format of "G" - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); - - var converter = new DateTimeConverter(DateTimeKind.Unspecified); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().BeOfType(); - ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); - ((DateTime)result).ToUniversalTime().Year.Should().Be(year); - ((DateTime)result).ToUniversalTime().Month.Should().Be(month); - ((DateTime)result).ToUniversalTime().Day.Should().Be(day); - ((DateTime)result).ToUniversalTime().Hour.Should().Be(hour); - ((DateTime)result).ToUniversalTime().Minute.Should().Be(minute); - ((DateTime)result).ToUniversalTime().Second.Should().Be(second); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Hour value. - /// Minute value. - /// Second value. - /// The converter instance uses its default parameter of "G". - [Theory] - [InlineData(2016, 12, 31, 3, 0, 0)] - public void Given_Yaml_WithValidDateTimeFormat_WithDefaultParameterAndLocal_ReadYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second) - { - var yaml = $"{month:00}/{day:00}/{year} {hour:00}:{minute:00}:{second:00}"; // This is the DateTime format of "G" - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); - - var converter = new DateTimeConverter(DateTimeKind.Local); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().BeOfType(); - ((DateTime)result).Kind.Should().Be(DateTimeKind.Local); - ((DateTime)result).ToLocalTime().Year.Should().Be(year); - ((DateTime)result).ToLocalTime().Month.Should().Be(month); - ((DateTime)result).ToLocalTime().Day.Should().Be(day); - ((DateTime)result).ToLocalTime().Hour.Should().Be(hour); - ((DateTime)result).ToLocalTime().Minute.Should().Be(minute); - ((DateTime)result).ToLocalTime().Second.Should().Be(second); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Designated date/time format 1. - /// Designated date/time format 2. - /// The converter instance uses its default parameter of UTC. - [Theory] - [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss")] - public void Given_Yaml_WithValidDateTimeFormat_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2) - { - var yaml = $"{year}-{month:00}-{day:00}"; - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); - - var converter = new DateTimeConverter(formats: new[] { format1, format2 }); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().BeOfType(); - ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); - ((DateTime)result).ToUniversalTime().Year.Should().Be(year); - ((DateTime)result).ToUniversalTime().Month.Should().Be(month); - ((DateTime)result).ToUniversalTime().Day.Should().Be(day); - ((DateTime)result).ToUniversalTime().Hour.Should().Be(0); - ((DateTime)result).ToUniversalTime().Minute.Should().Be(0); - ((DateTime)result).ToUniversalTime().Second.Should().Be(0); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Designated date/time format 1. - /// Designated date/time format 2. - /// The converter instance uses its default parameter of UTC. - [Theory] - [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss")] - public void Given_Yaml_WithSpecificCultureAndValidDateTimeFormat_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2) - { - var yaml = $"{year}-{month:00}-{day:00}"; - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); - - var culture = new CultureInfo("ko-KR"); // Sample specific culture - var converter = new DateTimeConverter(provider: culture, formats: new[] { format1, format2 }); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().BeOfType(); - ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); - ((DateTime)result).ToUniversalTime().Year.Should().Be(year); - ((DateTime)result).ToUniversalTime().Month.Should().Be(month); - ((DateTime)result).ToUniversalTime().Day.Should().Be(day); - ((DateTime)result).ToUniversalTime().Hour.Should().Be(0); - ((DateTime)result).ToUniversalTime().Minute.Should().Be(0); - ((DateTime)result).ToUniversalTime().Second.Should().Be(0); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Designated date/time format 1. - /// Designated date/time format 2. - /// The converter instance uses its default parameter of UTC. - [Theory] - [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss")] - public void Given_Yaml_WithValidDateTimeFormatAndUnspecified_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2) - { - var yaml = $"{year}-{month:00}-{day:00}"; - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); - - var converter = new DateTimeConverter(DateTimeKind.Unspecified, formats: new[] { format1, format2 }); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().BeOfType(); - ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); - ((DateTime)result).ToUniversalTime().Year.Should().Be(year); - ((DateTime)result).ToUniversalTime().Month.Should().Be(month); - ((DateTime)result).ToUniversalTime().Day.Should().Be(day); - ((DateTime)result).ToUniversalTime().Hour.Should().Be(0); - ((DateTime)result).ToUniversalTime().Minute.Should().Be(0); - ((DateTime)result).ToUniversalTime().Second.Should().Be(0); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Designated date/time format 1. - /// Designated date/time format 2. - /// The converter instance uses its default parameter of UTC. - [Theory] - [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss")] - public void Given_Yaml_WithValidDateTimeFormatAndLocal_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2) - { - var yaml = $"{year}-{month:00}-{day:00}"; - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); - - var converter = new DateTimeConverter(DateTimeKind.Local, formats: new[] { format1, format2 }); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().BeOfType(); - ((DateTime)result).Kind.Should().Be(DateTimeKind.Local); - ((DateTime)result).ToLocalTime().Year.Should().Be(year); - ((DateTime)result).ToLocalTime().Month.Should().Be(month); - ((DateTime)result).ToLocalTime().Day.Should().Be(day); - ((DateTime)result).ToLocalTime().Hour.Should().Be(0); - ((DateTime)result).ToLocalTime().Minute.Should().Be(0); - ((DateTime)result).ToLocalTime().Second.Should().Be(0); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Date/Time format. - /// Date/Time value. - /// Standard format "F" and "U" cannot be used at the same time as their string representations are identical to each other. - [Theory] - [InlineData("d", "01/11/2017")] - [InlineData("D", "Wednesday, 11 January 2017")] - [InlineData("f", "Wednesday, 11 January 2017 02:36")] - [InlineData("F", "Wednesday, 11 January 2017 02:36:16")] - [InlineData("g", "01/11/2017 02:36")] - [InlineData("G", "01/11/2017 02:36:16")] - [InlineData("M", "January 11")] - [InlineData("O", "2017-01-11T02:36:16.5065149+00:00")] - [InlineData("R", "Wed, 11 Jan 2017 02:36:16 GMT")] - [InlineData("s", "2017-01-11T02:36:16")] - [InlineData("t", "02:36")] - [InlineData("T", "02:36:16")] - [InlineData("u", "2017-01-11 02:36:16Z")] - [InlineData("U", "Wednesday, 11 January 2017 02:36:16")] - [InlineData("Y", "2017 January")] - public void Given_Yaml_WithTimeFormat_ReadYaml_ShouldReturn_Result(string format, string value) - { - var expected = DateTime.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToUniversalTime(); - var converter = new DateTimeConverter(formats: new[] { "d", "D", "f", "F", "g", "G", "M", "O", "R", "s", "t", "T", "u", "U", "Y" }); - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value)); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().Be(expected); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Date/Time format. - /// Locale value. - /// Date/Time value. - /// Standard format "F" and "U" cannot be used at the same time as their string representations are identical to each other. - [Theory] - [InlineData("d", "fr-FR", "13/01/2017")] - [InlineData("D", "fr-FR", "vendredi 13 janvier 2017")] - [InlineData("f", "fr-FR", "vendredi 13 janvier 2017 05:25")] - [InlineData("F", "fr-FR", "vendredi 13 janvier 2017 05:25:08")] - [InlineData("g", "fr-FR", "13/01/2017 05:25")] - [InlineData("G", "fr-FR", "13/01/2017 05:25:08")] - [InlineData("M", "fr-FR", "13 janvier")] - [InlineData("O", "fr-FR", "2017-01-13T05:25:08.2003629+00:00")] - [InlineData("R", "fr-FR", "Fri, 13 Jan 2017 05:25:08 GMT")] - [InlineData("s", "fr-FR", "2017-01-13T05:25:08")] - [InlineData("t", "fr-FR", "05:25")] - [InlineData("T", "fr-FR", "05:25:08")] - [InlineData("u", "fr-FR", "2017-01-13 05:25:08Z")] - [InlineData("U", "fr-FR", "vendredi 13 janvier 2017 05:25:08")] - [InlineData("Y", "fr-FR", "janvier 2017")] - // [InlineData("d", "ko-KR", "2017-01-13")] - [InlineData("D", "ko-KR", "2017년 1월 13일 금요일")] - // [InlineData("f", "ko-KR", "2017년 1월 13일 금요일 오전 5:32")] - // [InlineData("F", "ko-KR", "2017년 1월 13일 금요일 오전 5:32:06")] - // [InlineData("g", "ko-KR", "2017-01-13 오전 5:32")] - // [InlineData("G", "ko-KR", "2017-01-13 오전 5:32:06")] - [InlineData("M", "ko-KR", "1월 13일")] - [InlineData("O", "ko-KR", "2017-01-13T05:32:06.6865069+00:00")] - [InlineData("R", "ko-KR", "Fri, 13 Jan 2017 05:32:06 GMT")] - [InlineData("s", "ko-KR", "2017-01-13T05:32:06")] - // [InlineData("t", "ko-KR", "오전 5:32")] - // [InlineData("T", "ko-KR", "오전 5:32:06")] - [InlineData("u", "ko-KR", "2017-01-13 05:32:06Z")] - // [InlineData("U", "ko-KR", "2017년 1월 13일 금요일 오전 5:32:06")] - [InlineData("Y", "ko-KR", "2017년 1월")] - public void Given_Yaml_WithLocaleAndTimeFormat_ReadYaml_ShouldReturn_Result(string format, string locale, string value) - { - var culture = new CultureInfo(locale); - - var expected = default(DateTime); - try - { - expected = DateTime.ParseExact(value, format, culture, DateTimeStyles.AssumeUniversal).ToUniversalTime(); - } - catch (Exception ex) - { - var message = string.Format("Failed to parse the test argument to DateTime. The expected date format should look like this: '{0}'", DateTime.Now.ToString(format, culture)); - throw new Exception(message, ex); - } - - var converter = new DateTimeConverter(provider: culture, formats: new[] { "d", "D", "f", "F", "g", "G", "M", "O", "R", "s", "t", "T", "u", "U", "Y" }); - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value)); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().Be(expected); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Date/Time format. - /// Date/Time value. - /// Standard format "F" and "U" cannot be used at the same time as their string representations are identical to each other. - [Theory] - [InlineData("d", "01/11/2017")] - [InlineData("D", "Wednesday, 11 January 2017")] - [InlineData("f", "Wednesday, 11 January 2017 02:36")] - [InlineData("F", "Wednesday, 11 January 2017 02:36:16")] - [InlineData("g", "01/11/2017 02:36")] - [InlineData("G", "01/11/2017 02:36:16")] - [InlineData("M", "January 11")] - [InlineData("O", "2017-01-11T02:36:16.5065149+00:00")] - [InlineData("R", "Wed, 11 Jan 2017 02:36:16 GMT")] - [InlineData("s", "2017-01-11T02:36:16")] - [InlineData("t", "02:36")] - [InlineData("T", "02:36:16")] - [InlineData("u", "2017-01-11 02:36:16Z")] - [InlineData("Y", "2017 January")] - public void Given_Yaml_WithTimeFormatAndLocal1_ReadYaml_ShouldReturn_Result(string format, string value) - { - var expected = DateTime.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal).ToLocalTime(); - var converter = new DateTimeConverter(DateTimeKind.Local, formats: new[] { "d", "D", "f", "F", "g", "G", "M", "O", "R", "s", "t", "T", "u", "Y" }); - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value)); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().Be(expected); - } - - /// - /// Tests whether the ReadYaml() method should return expected result or not. - /// - /// Date/Time format. - /// Date/Time value. - /// Standard format "F" and "U" cannot be used at the same time as their string representations are identical to each other. - [Theory] - [InlineData("d", "01/11/2017")] - [InlineData("D", "Wednesday, 11 January 2017")] - [InlineData("f", "Wednesday, 11 January 2017 02:36")] - [InlineData("g", "01/11/2017 02:36")] - [InlineData("G", "01/11/2017 02:36:16")] - [InlineData("M", "January 11")] - [InlineData("O", "2017-01-11T02:36:16.5065149+00:00")] - [InlineData("R", "Wed, 11 Jan 2017 02:36:16 GMT")] - [InlineData("s", "2017-01-11T02:36:16")] - [InlineData("t", "02:36")] - [InlineData("T", "02:36:16")] - [InlineData("u", "2017-01-11 02:36:16Z")] - [InlineData("U", "Wednesday, 11 January 2017 02:36:16")] - [InlineData("Y", "2017 January")] - public void Given_Yaml_WithTimeFormatAndLocal2_ReadYaml_ShouldReturn_Result(string format, string value) - { - var expected = DateTime.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal).ToLocalTime(); - var converter = new DateTimeConverter(DateTimeKind.Local, formats: new[] { "d", "D", "f", "g", "G", "M", "O", "R", "s", "t", "T", "u", "U", "Y" }); - - var parser = A.Fake(); - A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value)); - - var result = converter.ReadYaml(parser, typeof(DateTime)); - - result.Should().Be(expected); - } - - /// - /// Tests whether the WriteYaml method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Hour value. - /// Minute value. - /// Second value. - /// value - /// The converter instance uses its default parameters of "G" and UTC. - [Theory] - [InlineData(2016, 12, 31, 3, 0, 0, DateTimeKind.Utc)] - public void Given_Values_WriteYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) - { - var dt = new DateTime(year, month, day, hour, minute, second, kind); - var formatted = dt.ToString("G", CultureInfo.InvariantCulture); - var obj = new TestObject() { DateTime = dt }; - - var builder = new SerializerBuilder(); - builder.WithNamingConvention(CamelCaseNamingConvention.Instance); - builder.WithTypeConverter(new DateTimeConverter()); - - var serialiser = builder.Build(); - - var serialised = serialiser.Serialize(obj); - - serialised.Should().ContainEquivalentOf($"datetime: {formatted}"); - } - - /// - /// Tests whether the WriteYaml method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Hour value. - /// Minute value. - /// Second value. - /// value - /// Locale value. - /// The converter instance uses its default parameters of "G" and UTC. - [Theory] - [InlineData(2016, 12, 31, 3, 0, 0, DateTimeKind.Utc, "es-ES")] - [InlineData(2016, 12, 31, 3, 0, 0, DateTimeKind.Utc, "ko-KR")] - public void Given_Values_WithLocale_WriteYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind, string locale) - { - var dt = new DateTime(year, month, day, hour, minute, second, kind); - var culture = new CultureInfo(locale); - var formatted = dt.ToString("G", culture); - var obj = new TestObject() { DateTime = dt }; - - var builder = new SerializerBuilder(); - builder.WithNamingConvention(CamelCaseNamingConvention.Instance); - builder.WithTypeConverter(new DateTimeConverter(provider: culture)); - - var serialiser = builder.Build(); - - var serialised = serialiser.Serialize(obj); - - serialised.Should().ContainEquivalentOf($"datetime: {formatted}"); - } - - /// - /// Tests whether the WriteYaml method should return expected result or not. - /// - /// Year value. - /// Month value. - /// Day value. - /// Hour value. - /// Minute value. - /// Second value. - /// value - /// The converter instance uses its default parameters of "G" and UTC. - [Theory] - [InlineData(2016, 12, 31, 3, 0, 0, DateTimeKind.Utc)] - public void Given_Values_WithFormats_WriteYaml_ShouldReturn_Result_WithFirstFormat(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) - { - var dt = new DateTime(year, month, day, hour, minute, second, kind); - var format = "yyyy-MM-dd HH:mm:ss"; - var formatted = dt.ToString(format, CultureInfo.InvariantCulture); - var obj = new TestObject() { DateTime = dt }; - - var builder = new SerializerBuilder(); - builder.WithNamingConvention(CamelCaseNamingConvention.Instance); - builder.WithTypeConverter(new DateTimeConverter(kind, formats: new[] { format, "G" })); - - var serialiser = builder.Build(); - - var serialised = serialiser.Serialize(obj); - - serialised.Should().ContainEquivalentOf($"datetime: {formatted}"); - } - } - - /// - /// This represents the test object entity. - /// - public class TestObject - { - /// - /// Gets or sets the value. - /// - public DateTime DateTime { get; set; } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Globalization; +using FakeItEasy; +using FluentAssertions; +using Xunit; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.Converters; +using YamlDotNet.Serialization.NamingConventions; + +namespace YamlDotNet.Test.Serialization +{ + /// + /// This represents the test entity for the class. + /// + public class DateTimeConverterTests + { + /// + /// Tests whether the Accepts() method should return expected result or not. + /// + /// to check. + /// Expected result. + [Theory] + [InlineData(typeof(DateTime), true)] + [InlineData(typeof(string), false)] + public void Given_Type_Accepts_ShouldReturn_Result(Type type, bool expected) + { + var converter = new DateTimeConverter(); + + var result = converter.Accepts(type); + + result.Should().Be(expected); + } + + /// + /// Tests whether the ReadYaml() method should throw or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// The converter instance uses its default parameters of "G" and UTC. + [Theory] + [InlineData(2016, 12, 31)] + public void Given_Yaml_WithInvalidDateTimeFormat_WithDefaultParameters_ReadYaml_ShouldThrow_Exception(int year, int month, int day) + { + var yaml = $"{year}-{month:00}-{day:00}"; + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); + + var converter = new DateTimeConverter(); + + Action action = () => { converter.ReadYaml(parser, typeof(DateTime)); }; + + action.ShouldThrow(); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Hour value. + /// Minute value. + /// Second value. + /// The converter instance uses its default parameters of "G" and UTC. + [Theory] + [InlineData(2016, 12, 31, 3, 0, 0)] + public void Given_Yaml_WithValidDateTimeFormat_WithDefaultParameters_ReadYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second) + { + var yaml = $"{month:00}/{day:00}/{year} {hour:00}:{minute:00}:{second:00}"; // This is the DateTime format of "G" + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); + + var converter = new DateTimeConverter(); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().BeOfType(); + ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); + ((DateTime)result).ToUniversalTime().Year.Should().Be(year); + ((DateTime)result).ToUniversalTime().Month.Should().Be(month); + ((DateTime)result).ToUniversalTime().Day.Should().Be(day); + ((DateTime)result).ToUniversalTime().Hour.Should().Be(hour); + ((DateTime)result).ToUniversalTime().Minute.Should().Be(minute); + ((DateTime)result).ToUniversalTime().Second.Should().Be(second); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Hour value. + /// Minute value. + /// Second value. + /// The converter instance uses its default parameters of "G". + [Theory] + [InlineData(2016, 12, 31, 3, 0, 0)] + public void Given_Yaml_WithValidDateTimeFormat_WithDefaultParameterAndUnspecified_ReadYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second) + { + var yaml = $"{month:00}/{day:00}/{year} {hour:00}:{minute:00}:{second:00}"; // This is the DateTime format of "G" + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); + + var converter = new DateTimeConverter(DateTimeKind.Unspecified); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().BeOfType(); + ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); + ((DateTime)result).ToUniversalTime().Year.Should().Be(year); + ((DateTime)result).ToUniversalTime().Month.Should().Be(month); + ((DateTime)result).ToUniversalTime().Day.Should().Be(day); + ((DateTime)result).ToUniversalTime().Hour.Should().Be(hour); + ((DateTime)result).ToUniversalTime().Minute.Should().Be(minute); + ((DateTime)result).ToUniversalTime().Second.Should().Be(second); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Hour value. + /// Minute value. + /// Second value. + /// The converter instance uses its default parameter of "G". + [Theory] + [InlineData(2016, 12, 31, 3, 0, 0)] + public void Given_Yaml_WithValidDateTimeFormat_WithDefaultParameterAndLocal_ReadYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second) + { + var yaml = $"{month:00}/{day:00}/{year} {hour:00}:{minute:00}:{second:00}"; // This is the DateTime format of "G" + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); + + var converter = new DateTimeConverter(DateTimeKind.Local); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().BeOfType(); + ((DateTime)result).Kind.Should().Be(DateTimeKind.Local); + ((DateTime)result).ToLocalTime().Year.Should().Be(year); + ((DateTime)result).ToLocalTime().Month.Should().Be(month); + ((DateTime)result).ToLocalTime().Day.Should().Be(day); + ((DateTime)result).ToLocalTime().Hour.Should().Be(hour); + ((DateTime)result).ToLocalTime().Minute.Should().Be(minute); + ((DateTime)result).ToLocalTime().Second.Should().Be(second); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Designated date/time format 1. + /// Designated date/time format 2. + /// The converter instance uses its default parameter of UTC. + [Theory] + [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss")] + public void Given_Yaml_WithValidDateTimeFormat_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2) + { + var yaml = $"{year}-{month:00}-{day:00}"; + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); + + var converter = new DateTimeConverter(formats: new[] { format1, format2 }); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().BeOfType(); + ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); + ((DateTime)result).ToUniversalTime().Year.Should().Be(year); + ((DateTime)result).ToUniversalTime().Month.Should().Be(month); + ((DateTime)result).ToUniversalTime().Day.Should().Be(day); + ((DateTime)result).ToUniversalTime().Hour.Should().Be(0); + ((DateTime)result).ToUniversalTime().Minute.Should().Be(0); + ((DateTime)result).ToUniversalTime().Second.Should().Be(0); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Designated date/time format 1. + /// Designated date/time format 2. + /// The converter instance uses its default parameter of UTC. + [Theory] + [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss")] + public void Given_Yaml_WithSpecificCultureAndValidDateTimeFormat_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2) + { + var yaml = $"{year}-{month:00}-{day:00}"; + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); + + var culture = new CultureInfo("ko-KR"); // Sample specific culture + var converter = new DateTimeConverter(provider: culture, formats: new[] { format1, format2 }); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().BeOfType(); + ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); + ((DateTime)result).ToUniversalTime().Year.Should().Be(year); + ((DateTime)result).ToUniversalTime().Month.Should().Be(month); + ((DateTime)result).ToUniversalTime().Day.Should().Be(day); + ((DateTime)result).ToUniversalTime().Hour.Should().Be(0); + ((DateTime)result).ToUniversalTime().Minute.Should().Be(0); + ((DateTime)result).ToUniversalTime().Second.Should().Be(0); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Designated date/time format 1. + /// Designated date/time format 2. + /// The converter instance uses its default parameter of UTC. + [Theory] + [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss")] + public void Given_Yaml_WithValidDateTimeFormatAndUnspecified_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2) + { + var yaml = $"{year}-{month:00}-{day:00}"; + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); + + var converter = new DateTimeConverter(DateTimeKind.Unspecified, formats: new[] { format1, format2 }); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().BeOfType(); + ((DateTime)result).Kind.Should().Be(DateTimeKind.Utc); + ((DateTime)result).ToUniversalTime().Year.Should().Be(year); + ((DateTime)result).ToUniversalTime().Month.Should().Be(month); + ((DateTime)result).ToUniversalTime().Day.Should().Be(day); + ((DateTime)result).ToUniversalTime().Hour.Should().Be(0); + ((DateTime)result).ToUniversalTime().Minute.Should().Be(0); + ((DateTime)result).ToUniversalTime().Second.Should().Be(0); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Designated date/time format 1. + /// Designated date/time format 2. + /// The converter instance uses its default parameter of UTC. + [Theory] + [InlineData(2016, 12, 31, "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss")] + public void Given_Yaml_WithValidDateTimeFormatAndLocal_ReadYaml_ShouldReturn_Result(int year, int month, int day, string format1, string format2) + { + var yaml = $"{year}-{month:00}-{day:00}"; + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(yaml)); + + var converter = new DateTimeConverter(DateTimeKind.Local, formats: new[] { format1, format2 }); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().BeOfType(); + ((DateTime)result).Kind.Should().Be(DateTimeKind.Local); + ((DateTime)result).ToLocalTime().Year.Should().Be(year); + ((DateTime)result).ToLocalTime().Month.Should().Be(month); + ((DateTime)result).ToLocalTime().Day.Should().Be(day); + ((DateTime)result).ToLocalTime().Hour.Should().Be(0); + ((DateTime)result).ToLocalTime().Minute.Should().Be(0); + ((DateTime)result).ToLocalTime().Second.Should().Be(0); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Date/Time format. + /// Date/Time value. + /// Standard format "F" and "U" cannot be used at the same time as their string representations are identical to each other. + [Theory] + [InlineData("d", "01/11/2017")] + [InlineData("D", "Wednesday, 11 January 2017")] + [InlineData("f", "Wednesday, 11 January 2017 02:36")] + [InlineData("F", "Wednesday, 11 January 2017 02:36:16")] + [InlineData("g", "01/11/2017 02:36")] + [InlineData("G", "01/11/2017 02:36:16")] + [InlineData("M", "January 11")] + [InlineData("O", "2017-01-11T02:36:16.5065149+00:00")] + [InlineData("R", "Wed, 11 Jan 2017 02:36:16 GMT")] + [InlineData("s", "2017-01-11T02:36:16")] + [InlineData("t", "02:36")] + [InlineData("T", "02:36:16")] + [InlineData("u", "2017-01-11 02:36:16Z")] + [InlineData("U", "Wednesday, 11 January 2017 02:36:16")] + [InlineData("Y", "2017 January")] + public void Given_Yaml_WithTimeFormat_ReadYaml_ShouldReturn_Result(string format, string value) + { + var expected = DateTime.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToUniversalTime(); + var converter = new DateTimeConverter(formats: new[] { "d", "D", "f", "F", "g", "G", "M", "O", "R", "s", "t", "T", "u", "U", "Y" }); + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value)); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().Be(expected); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Date/Time format. + /// Locale value. + /// Date/Time value. + /// Standard format "F" and "U" cannot be used at the same time as their string representations are identical to each other. + [Theory] + [InlineData("d", "fr-FR", "13/01/2017")] + [InlineData("D", "fr-FR", "vendredi 13 janvier 2017")] + [InlineData("f", "fr-FR", "vendredi 13 janvier 2017 05:25")] + [InlineData("F", "fr-FR", "vendredi 13 janvier 2017 05:25:08")] + [InlineData("g", "fr-FR", "13/01/2017 05:25")] + [InlineData("G", "fr-FR", "13/01/2017 05:25:08")] + [InlineData("M", "fr-FR", "13 janvier")] + [InlineData("O", "fr-FR", "2017-01-13T05:25:08.2003629+00:00")] + [InlineData("R", "fr-FR", "Fri, 13 Jan 2017 05:25:08 GMT")] + [InlineData("s", "fr-FR", "2017-01-13T05:25:08")] + [InlineData("t", "fr-FR", "05:25")] + [InlineData("T", "fr-FR", "05:25:08")] + [InlineData("u", "fr-FR", "2017-01-13 05:25:08Z")] + [InlineData("U", "fr-FR", "vendredi 13 janvier 2017 05:25:08")] + [InlineData("Y", "fr-FR", "janvier 2017")] + // [InlineData("d", "ko-KR", "2017-01-13")] + [InlineData("D", "ko-KR", "2017년 1월 13일 금요일")] + // [InlineData("f", "ko-KR", "2017년 1월 13일 금요일 오전 5:32")] + // [InlineData("F", "ko-KR", "2017년 1월 13일 금요일 오전 5:32:06")] + // [InlineData("g", "ko-KR", "2017-01-13 오전 5:32")] + // [InlineData("G", "ko-KR", "2017-01-13 오전 5:32:06")] + [InlineData("M", "ko-KR", "1월 13일")] + [InlineData("O", "ko-KR", "2017-01-13T05:32:06.6865069+00:00")] + [InlineData("R", "ko-KR", "Fri, 13 Jan 2017 05:32:06 GMT")] + [InlineData("s", "ko-KR", "2017-01-13T05:32:06")] + // [InlineData("t", "ko-KR", "오전 5:32")] + // [InlineData("T", "ko-KR", "오전 5:32:06")] + [InlineData("u", "ko-KR", "2017-01-13 05:32:06Z")] + // [InlineData("U", "ko-KR", "2017년 1월 13일 금요일 오전 5:32:06")] + [InlineData("Y", "ko-KR", "2017년 1월")] + public void Given_Yaml_WithLocaleAndTimeFormat_ReadYaml_ShouldReturn_Result(string format, string locale, string value) + { + var culture = new CultureInfo(locale); + + var expected = default(DateTime); + try + { + expected = DateTime.ParseExact(value, format, culture, DateTimeStyles.AssumeUniversal).ToUniversalTime(); + } + catch (Exception ex) + { + var message = string.Format("Failed to parse the test argument to DateTime. The expected date format should look like this: '{0}'", DateTime.Now.ToString(format, culture)); + throw new Exception(message, ex); + } + + var converter = new DateTimeConverter(provider: culture, formats: new[] { "d", "D", "f", "F", "g", "G", "M", "O", "R", "s", "t", "T", "u", "U", "Y" }); + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value)); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().Be(expected); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Date/Time format. + /// Date/Time value. + /// Standard format "F" and "U" cannot be used at the same time as their string representations are identical to each other. + [Theory] + [InlineData("d", "01/11/2017")] + [InlineData("D", "Wednesday, 11 January 2017")] + [InlineData("f", "Wednesday, 11 January 2017 02:36")] + [InlineData("F", "Wednesday, 11 January 2017 02:36:16")] + [InlineData("g", "01/11/2017 02:36")] + [InlineData("G", "01/11/2017 02:36:16")] + [InlineData("M", "January 11")] + [InlineData("O", "2017-01-11T02:36:16.5065149+00:00")] + [InlineData("R", "Wed, 11 Jan 2017 02:36:16 GMT")] + [InlineData("s", "2017-01-11T02:36:16")] + [InlineData("t", "02:36")] + [InlineData("T", "02:36:16")] + [InlineData("u", "2017-01-11 02:36:16Z")] + [InlineData("Y", "2017 January")] + public void Given_Yaml_WithTimeFormatAndLocal1_ReadYaml_ShouldReturn_Result(string format, string value) + { + var expected = DateTime.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal).ToLocalTime(); + var converter = new DateTimeConverter(DateTimeKind.Local, formats: new[] { "d", "D", "f", "F", "g", "G", "M", "O", "R", "s", "t", "T", "u", "Y" }); + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value)); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().Be(expected); + } + + /// + /// Tests whether the ReadYaml() method should return expected result or not. + /// + /// Date/Time format. + /// Date/Time value. + /// Standard format "F" and "U" cannot be used at the same time as their string representations are identical to each other. + [Theory] + [InlineData("d", "01/11/2017")] + [InlineData("D", "Wednesday, 11 January 2017")] + [InlineData("f", "Wednesday, 11 January 2017 02:36")] + [InlineData("g", "01/11/2017 02:36")] + [InlineData("G", "01/11/2017 02:36:16")] + [InlineData("M", "January 11")] + [InlineData("O", "2017-01-11T02:36:16.5065149+00:00")] + [InlineData("R", "Wed, 11 Jan 2017 02:36:16 GMT")] + [InlineData("s", "2017-01-11T02:36:16")] + [InlineData("t", "02:36")] + [InlineData("T", "02:36:16")] + [InlineData("u", "2017-01-11 02:36:16Z")] + [InlineData("U", "Wednesday, 11 January 2017 02:36:16")] + [InlineData("Y", "2017 January")] + public void Given_Yaml_WithTimeFormatAndLocal2_ReadYaml_ShouldReturn_Result(string format, string value) + { + var expected = DateTime.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal).ToLocalTime(); + var converter = new DateTimeConverter(DateTimeKind.Local, formats: new[] { "d", "D", "f", "g", "G", "M", "O", "R", "s", "t", "T", "u", "U", "Y" }); + + var parser = A.Fake(); + A.CallTo(() => parser.Current).ReturnsLazily(() => new Scalar(value)); + + var result = converter.ReadYaml(parser, typeof(DateTime)); + + result.Should().Be(expected); + } + + /// + /// Tests whether the WriteYaml method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Hour value. + /// Minute value. + /// Second value. + /// value + /// The converter instance uses its default parameters of "G" and UTC. + [Theory] + [InlineData(2016, 12, 31, 3, 0, 0, DateTimeKind.Utc)] + public void Given_Values_WriteYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + { + var dt = new DateTime(year, month, day, hour, minute, second, kind); + var formatted = dt.ToString("G", CultureInfo.InvariantCulture); + var obj = new TestObject() { DateTime = dt }; + + var builder = new SerializerBuilder(); + builder.WithNamingConvention(CamelCaseNamingConvention.Instance); + builder.WithTypeConverter(new DateTimeConverter()); + + var serialiser = builder.Build(); + + var serialised = serialiser.Serialize(obj); + + serialised.Should().ContainEquivalentOf($"datetime: {formatted}"); + } + + /// + /// Tests whether the WriteYaml method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Hour value. + /// Minute value. + /// Second value. + /// value + /// Locale value. + /// The converter instance uses its default parameters of "G" and UTC. + [Theory] + [InlineData(2016, 12, 31, 3, 0, 0, DateTimeKind.Utc, "es-ES")] + [InlineData(2016, 12, 31, 3, 0, 0, DateTimeKind.Utc, "ko-KR")] + public void Given_Values_WithLocale_WriteYaml_ShouldReturn_Result(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind, string locale) + { + var dt = new DateTime(year, month, day, hour, minute, second, kind); + var culture = new CultureInfo(locale); + var formatted = dt.ToString("G", culture); + var obj = new TestObject() { DateTime = dt }; + + var builder = new SerializerBuilder(); + builder.WithNamingConvention(CamelCaseNamingConvention.Instance); + builder.WithTypeConverter(new DateTimeConverter(provider: culture)); + + var serialiser = builder.Build(); + + var serialised = serialiser.Serialize(obj); + + serialised.Should().ContainEquivalentOf($"datetime: {formatted}"); + } + + /// + /// Tests whether the WriteYaml method should return expected result or not. + /// + /// Year value. + /// Month value. + /// Day value. + /// Hour value. + /// Minute value. + /// Second value. + /// value + /// The converter instance uses its default parameters of "G" and UTC. + [Theory] + [InlineData(2016, 12, 31, 3, 0, 0, DateTimeKind.Utc)] + public void Given_Values_WithFormats_WriteYaml_ShouldReturn_Result_WithFirstFormat(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + { + var dt = new DateTime(year, month, day, hour, minute, second, kind); + var format = "yyyy-MM-dd HH:mm:ss"; + var formatted = dt.ToString(format, CultureInfo.InvariantCulture); + var obj = new TestObject() { DateTime = dt }; + + var builder = new SerializerBuilder(); + builder.WithNamingConvention(CamelCaseNamingConvention.Instance); + builder.WithTypeConverter(new DateTimeConverter(kind, formats: new[] { format, "G" })); + + var serialiser = builder.Build(); + + var serialised = serialiser.Serialize(obj); + + serialised.Should().ContainEquivalentOf($"datetime: {formatted}"); + } + } + + /// + /// This represents the test object entity. + /// + public class TestObject + { + /// + /// Gets or sets the value. + /// + public DateTime DateTime { get; set; } + } +} diff --git a/YamlDotNet.Test/Serialization/DeserializerTest.cs b/YamlDotNet.Test/Serialization/DeserializerTest.cs index 7a9324929..30046df73 100644 --- a/YamlDotNet.Test/Serialization/DeserializerTest.cs +++ b/YamlDotNet.Test/Serialization/DeserializerTest.cs @@ -1,314 +1,314 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using FluentAssertions; -using Xunit; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - -namespace YamlDotNet.Test.Serialization -{ - public class DeserializerTest - { - [Fact] - public void Deserialize_YamlWithInterfaceTypeAndMapping_ReturnsModel() - { - var yaml = @" -name: Jack -momentOfBirth: 1983-04-21T20:21:03.0041599Z -cars: -- name: Mercedes - year: 2018 -- name: Honda - year: 2021 -"; - - var sut = new DeserializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .WithTypeMapping() - .Build(); - - var person = sut.Deserialize(yaml); - person.Name.Should().Be("Jack"); - person.MomentOfBirth.Kind.Should().Be(DateTimeKind.Utc); - person.MomentOfBirth.ToUniversalTime().Year.Should().Be(1983); - person.MomentOfBirth.ToUniversalTime().Month.Should().Be(4); - person.MomentOfBirth.ToUniversalTime().Day.Should().Be(21); - person.MomentOfBirth.ToUniversalTime().Hour.Should().Be(20); - person.MomentOfBirth.ToUniversalTime().Minute.Should().Be(21); - person.MomentOfBirth.ToUniversalTime().Second.Should().Be(3); - person.Cars.Should().HaveCount(2); - person.Cars[0].Name.Should().Be("Mercedes"); - person.Cars[0].Spec.Should().BeNull(); - person.Cars[1].Name.Should().Be("Honda"); - person.Cars[1].Spec.Should().BeNull(); - } - - [Fact] - public void Deserialize_YamlWithTwoInterfaceTypesAndMappings_ReturnsModel() - { - var yaml = @" -name: Jack -momentOfBirth: 1983-04-21T20:21:03.0041599Z -cars: -- name: Mercedes - year: 2018 - spec: - engineType: V6 - driveType: AWD -- name: Honda - year: 2021 - spec: - engineType: V4 - driveType: FWD -"; - - var sut = new DeserializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .WithTypeMapping() - .WithTypeMapping() - .Build(); - - var person = sut.Deserialize(yaml); - person.Name.Should().Be("Jack"); - person.MomentOfBirth.Kind.Should().Be(DateTimeKind.Utc); - person.MomentOfBirth.ToUniversalTime().Year.Should().Be(1983); - person.MomentOfBirth.ToUniversalTime().Month.Should().Be(4); - person.MomentOfBirth.ToUniversalTime().Day.Should().Be(21); - person.MomentOfBirth.ToUniversalTime().Hour.Should().Be(20); - person.MomentOfBirth.ToUniversalTime().Minute.Should().Be(21); - person.MomentOfBirth.ToUniversalTime().Second.Should().Be(3); - person.Cars.Should().HaveCount(2); - person.Cars[0].Name.Should().Be("Mercedes"); - person.Cars[0].Spec.EngineType.Should().Be("V6"); - person.Cars[0].Spec.DriveType.Should().Be("AWD"); - person.Cars[1].Name.Should().Be("Honda"); - person.Cars[1].Spec.EngineType.Should().Be("V4"); - person.Cars[1].Spec.DriveType.Should().Be("FWD"); - } - - [Fact] - public void SetterOnlySetsWithoutException() - { - var yaml = @" -Value: bar -"; - var deserializer = new DeserializerBuilder().Build(); - var result = deserializer.Deserialize(yaml); - result.Actual.Should().Be("bar"); - } - - [Fact] - public void KeysOnDynamicClassDontGetQuoted() - { - var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); - var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); - var yaml = @" -True: null -False: hello -Null: true -X: -"; - var obj = deserializer.Deserialize(yaml, typeof(object)); - var result = serializer.Serialize(obj); - var dictionary = (Dictionary)obj; - var keys = dictionary.Keys.ToArray(); - Assert.Equal(keys, new[] { "True", "False", "Null", "X" }); - Assert.Equal(dictionary.Values, new object[] { null, "hello", true, null }); - } - - [Fact] - public void EmptyQuotedStringsArentNull() - { - var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); - var yaml = "Value: \"\""; - var result = deserializer.Deserialize(yaml); - Assert.Equal(string.Empty, result.Value); - } - - [Fact] - public void KeyAnchorIsHandledWithTypeDeserialization() - { - var yaml = @"a: &some_scalar this is also a key -b: &number 1 -*some_scalar: ""will this key be handled correctly?"" -*number: 1"; - var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); - var result = deserializer.Deserialize(yaml, typeof(object)); - Assert.IsType>(result); - var dictionary = (Dictionary)result; - Assert.Equal(new object[] { "a", "b", "this is also a key", (byte)1 }, dictionary.Keys); - Assert.Equal(new object[] { "this is also a key", (byte)1, "will this key be handled correctly?", (byte)1 }, dictionary.Values); - } - - [Fact] - public void NonScalarKeyIsHandledWithTypeDeserialization() - { - var yaml = @"scalar: foo -{ a: mapping }: bar -[ a, sequence, 1 ]: baz"; - var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); - var result = deserializer.Deserialize(yaml, typeof(object)); - Assert.IsType>(result); - - var dictionary = (Dictionary)result; - var item = dictionary.ElementAt(0); - Assert.Equal("scalar", item.Key); - Assert.Equal("foo", item.Value); - - item = dictionary.ElementAt(1); - Assert.IsType>(item.Key); - Assert.Equal("bar", item.Value); - dictionary = (Dictionary)item.Key; - item = dictionary.ElementAt(0); - Assert.Equal("a", item.Key); - Assert.Equal("mapping", item.Value); - - dictionary = (Dictionary)result; - item = dictionary.ElementAt(2); - Assert.IsType>(item.Key); - Assert.Equal(new List { "a", "sequence", (byte)1 }, (List)item.Key); - Assert.Equal("baz", item.Value); - } - - [Fact] - public void NewLinesInKeys() - { - var yaml = @"? >- - key - - a - - b -: >- - value - - a - - b -"; - var deserializer = new DeserializerBuilder().Build(); - var o = deserializer.Deserialize(yaml, typeof(object)); - Assert.IsType>(o); - var dictionary = (Dictionary)o; - Assert.Equal($"key\na\nb", dictionary.First().Key); - Assert.Equal($"value\na\nb", dictionary.First().Value); - } - - public static IEnumerable DeserializeScalarEdgeCases_TestCases - { - get - { - yield return new object[] { byte.MinValue, typeof(byte) }; - yield return new object[] { byte.MaxValue, typeof(byte) }; - yield return new object[] { short.MinValue, typeof(short) }; - yield return new object[] { short.MaxValue, typeof(short) }; - yield return new object[] { int.MinValue, typeof(int) }; - yield return new object[] { int.MaxValue, typeof(int) }; - yield return new object[] { long.MinValue, typeof(long) }; - yield return new object[] { long.MaxValue, typeof(long) }; - yield return new object[] { sbyte.MinValue, typeof(sbyte) }; - yield return new object[] { sbyte.MaxValue, typeof(sbyte) }; - yield return new object[] { ushort.MinValue, typeof(ushort) }; - yield return new object[] { ushort.MaxValue, typeof(ushort) }; - yield return new object[] { uint.MinValue, typeof(uint) }; - yield return new object[] { uint.MaxValue, typeof(uint) }; - yield return new object[] { ulong.MinValue, typeof(ulong) }; - yield return new object[] { ulong.MaxValue, typeof(ulong) }; - yield return new object[] { decimal.MinValue, typeof(decimal) }; - yield return new object[] { decimal.MaxValue, typeof(decimal) }; - yield return new object[] { char.MaxValue, typeof(char) }; - -#if NETCOREAPP3_1_OR_GREATER - yield return new object[] { float.MinValue, typeof(float) }; - yield return new object[] { float.MaxValue, typeof(float) }; - yield return new object[] { double.MinValue, typeof(double) }; - yield return new object[] { double.MaxValue, typeof(double) }; -#endif - } - } - - [Theory] - [MemberData(nameof(DeserializeScalarEdgeCases_TestCases))] - public void DeserializeScalarEdgeCases(IConvertible value, Type type) - { - var deserializer = new DeserializerBuilder().Build(); - var result = deserializer.Deserialize(value.ToString(), type); - - result.Should().Be(value); - } - - public class Test - { - public string Value { get; set; } - } - - public class SetterOnly - { - private string _value; - public string Value { set => _value = value; } - public string Actual { get => _value; } - } - - public class Person - { - public string Name { get; private set; } - - public DateTime MomentOfBirth { get; private set; } - - public IList Cars { get; private set; } - } - - public class Car : ICar - { - public string Name { get; private set; } - - public int Year { get; private set; } - - public IModelSpec Spec { get; private set; } - } - - public interface ICar - { - string Name { get; } - - int Year { get; } - IModelSpec Spec { get; } - } - - public class ModelSpec : IModelSpec - { - public string EngineType { get; private set; } - - public string DriveType { get; private set; } - } - - public interface IModelSpec - { - string EngineType { get; } - - string DriveType { get; } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Xunit; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace YamlDotNet.Test.Serialization +{ + public class DeserializerTest + { + [Fact] + public void Deserialize_YamlWithInterfaceTypeAndMapping_ReturnsModel() + { + var yaml = @" +name: Jack +momentOfBirth: 1983-04-21T20:21:03.0041599Z +cars: +- name: Mercedes + year: 2018 +- name: Honda + year: 2021 +"; + + var sut = new DeserializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .WithTypeMapping() + .Build(); + + var person = sut.Deserialize(yaml); + person.Name.Should().Be("Jack"); + person.MomentOfBirth.Kind.Should().Be(DateTimeKind.Utc); + person.MomentOfBirth.ToUniversalTime().Year.Should().Be(1983); + person.MomentOfBirth.ToUniversalTime().Month.Should().Be(4); + person.MomentOfBirth.ToUniversalTime().Day.Should().Be(21); + person.MomentOfBirth.ToUniversalTime().Hour.Should().Be(20); + person.MomentOfBirth.ToUniversalTime().Minute.Should().Be(21); + person.MomentOfBirth.ToUniversalTime().Second.Should().Be(3); + person.Cars.Should().HaveCount(2); + person.Cars[0].Name.Should().Be("Mercedes"); + person.Cars[0].Spec.Should().BeNull(); + person.Cars[1].Name.Should().Be("Honda"); + person.Cars[1].Spec.Should().BeNull(); + } + + [Fact] + public void Deserialize_YamlWithTwoInterfaceTypesAndMappings_ReturnsModel() + { + var yaml = @" +name: Jack +momentOfBirth: 1983-04-21T20:21:03.0041599Z +cars: +- name: Mercedes + year: 2018 + spec: + engineType: V6 + driveType: AWD +- name: Honda + year: 2021 + spec: + engineType: V4 + driveType: FWD +"; + + var sut = new DeserializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .WithTypeMapping() + .WithTypeMapping() + .Build(); + + var person = sut.Deserialize(yaml); + person.Name.Should().Be("Jack"); + person.MomentOfBirth.Kind.Should().Be(DateTimeKind.Utc); + person.MomentOfBirth.ToUniversalTime().Year.Should().Be(1983); + person.MomentOfBirth.ToUniversalTime().Month.Should().Be(4); + person.MomentOfBirth.ToUniversalTime().Day.Should().Be(21); + person.MomentOfBirth.ToUniversalTime().Hour.Should().Be(20); + person.MomentOfBirth.ToUniversalTime().Minute.Should().Be(21); + person.MomentOfBirth.ToUniversalTime().Second.Should().Be(3); + person.Cars.Should().HaveCount(2); + person.Cars[0].Name.Should().Be("Mercedes"); + person.Cars[0].Spec.EngineType.Should().Be("V6"); + person.Cars[0].Spec.DriveType.Should().Be("AWD"); + person.Cars[1].Name.Should().Be("Honda"); + person.Cars[1].Spec.EngineType.Should().Be("V4"); + person.Cars[1].Spec.DriveType.Should().Be("FWD"); + } + + [Fact] + public void SetterOnlySetsWithoutException() + { + var yaml = @" +Value: bar +"; + var deserializer = new DeserializerBuilder().Build(); + var result = deserializer.Deserialize(yaml); + result.Actual.Should().Be("bar"); + } + + [Fact] + public void KeysOnDynamicClassDontGetQuoted() + { + var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); + var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); + var yaml = @" +True: null +False: hello +Null: true +X: +"; + var obj = deserializer.Deserialize(yaml, typeof(object)); + var result = serializer.Serialize(obj); + var dictionary = (Dictionary)obj; + var keys = dictionary.Keys.ToArray(); + Assert.Equal(keys, new[] { "True", "False", "Null", "X" }); + Assert.Equal(dictionary.Values, new object[] { null, "hello", true, null }); + } + + [Fact] + public void EmptyQuotedStringsArentNull() + { + var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); + var yaml = "Value: \"\""; + var result = deserializer.Deserialize(yaml); + Assert.Equal(string.Empty, result.Value); + } + + [Fact] + public void KeyAnchorIsHandledWithTypeDeserialization() + { + var yaml = @"a: &some_scalar this is also a key +b: &number 1 +*some_scalar: ""will this key be handled correctly?"" +*number: 1"; + var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); + var result = deserializer.Deserialize(yaml, typeof(object)); + Assert.IsType>(result); + var dictionary = (Dictionary)result; + Assert.Equal(new object[] { "a", "b", "this is also a key", (byte)1 }, dictionary.Keys); + Assert.Equal(new object[] { "this is also a key", (byte)1, "will this key be handled correctly?", (byte)1 }, dictionary.Values); + } + + [Fact] + public void NonScalarKeyIsHandledWithTypeDeserialization() + { + var yaml = @"scalar: foo +{ a: mapping }: bar +[ a, sequence, 1 ]: baz"; + var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); + var result = deserializer.Deserialize(yaml, typeof(object)); + Assert.IsType>(result); + + var dictionary = (Dictionary)result; + var item = dictionary.ElementAt(0); + Assert.Equal("scalar", item.Key); + Assert.Equal("foo", item.Value); + + item = dictionary.ElementAt(1); + Assert.IsType>(item.Key); + Assert.Equal("bar", item.Value); + dictionary = (Dictionary)item.Key; + item = dictionary.ElementAt(0); + Assert.Equal("a", item.Key); + Assert.Equal("mapping", item.Value); + + dictionary = (Dictionary)result; + item = dictionary.ElementAt(2); + Assert.IsType>(item.Key); + Assert.Equal(new List { "a", "sequence", (byte)1 }, (List)item.Key); + Assert.Equal("baz", item.Value); + } + + [Fact] + public void NewLinesInKeys() + { + var yaml = @"? >- + key + + a + + b +: >- + value + + a + + b +"; + var deserializer = new DeserializerBuilder().Build(); + var o = deserializer.Deserialize(yaml, typeof(object)); + Assert.IsType>(o); + var dictionary = (Dictionary)o; + Assert.Equal($"key\na\nb", dictionary.First().Key); + Assert.Equal($"value\na\nb", dictionary.First().Value); + } + + public static IEnumerable DeserializeScalarEdgeCases_TestCases + { + get + { + yield return new object[] { byte.MinValue, typeof(byte) }; + yield return new object[] { byte.MaxValue, typeof(byte) }; + yield return new object[] { short.MinValue, typeof(short) }; + yield return new object[] { short.MaxValue, typeof(short) }; + yield return new object[] { int.MinValue, typeof(int) }; + yield return new object[] { int.MaxValue, typeof(int) }; + yield return new object[] { long.MinValue, typeof(long) }; + yield return new object[] { long.MaxValue, typeof(long) }; + yield return new object[] { sbyte.MinValue, typeof(sbyte) }; + yield return new object[] { sbyte.MaxValue, typeof(sbyte) }; + yield return new object[] { ushort.MinValue, typeof(ushort) }; + yield return new object[] { ushort.MaxValue, typeof(ushort) }; + yield return new object[] { uint.MinValue, typeof(uint) }; + yield return new object[] { uint.MaxValue, typeof(uint) }; + yield return new object[] { ulong.MinValue, typeof(ulong) }; + yield return new object[] { ulong.MaxValue, typeof(ulong) }; + yield return new object[] { decimal.MinValue, typeof(decimal) }; + yield return new object[] { decimal.MaxValue, typeof(decimal) }; + yield return new object[] { char.MaxValue, typeof(char) }; + +#if NETCOREAPP3_1_OR_GREATER + yield return new object[] { float.MinValue, typeof(float) }; + yield return new object[] { float.MaxValue, typeof(float) }; + yield return new object[] { double.MinValue, typeof(double) }; + yield return new object[] { double.MaxValue, typeof(double) }; +#endif + } + } + + [Theory] + [MemberData(nameof(DeserializeScalarEdgeCases_TestCases))] + public void DeserializeScalarEdgeCases(IConvertible value, Type type) + { + var deserializer = new DeserializerBuilder().Build(); + var result = deserializer.Deserialize(value.ToString(), type); + + result.Should().Be(value); + } + + public class Test + { + public string Value { get; set; } + } + + public class SetterOnly + { + private string _value; + public string Value { set => _value = value; } + public string Actual { get => _value; } + } + + public class Person + { + public string Name { get; private set; } + + public DateTime MomentOfBirth { get; private set; } + + public IList Cars { get; private set; } + } + + public class Car : ICar + { + public string Name { get; private set; } + + public int Year { get; private set; } + + public IModelSpec Spec { get; private set; } + } + + public interface ICar + { + string Name { get; } + + int Year { get; } + IModelSpec Spec { get; } + } + + public class ModelSpec : IModelSpec + { + public string EngineType { get; private set; } + + public string DriveType { get; private set; } + } + + public interface IModelSpec + { + string EngineType { get; } + + string DriveType { get; } + } + } +} diff --git a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs index a74cad3d2..fd0fd7e63 100644 --- a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs +++ b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs @@ -1,188 +1,188 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using Xunit; -using YamlDotNet.Serialization; - -namespace YamlDotNet.Test.Serialization -{ - public class EmitDefaultValuesTests - { - private class Model - { - public string ANullString => null; - [DefaultValue("hello")] public string ADefaultString => "hello"; - [DefaultValue("hello")] public string ANonDefaultString => "world"; - [DefaultValue("hello")] public string ANonDefaultNullString => null; - - public int AZeroInteger => 0; - public int ANonZeroInteger => 1; - [DefaultValue(2)] public int ADefaultInteger => 2; - [DefaultValue(2)] public int ANonDefaultZeroInteger => 0; - - public int? ANullInteger => null; - public int? ANullableZeroInteger => 0; - public int? ANullableNonZeroInteger => 1; - [DefaultValue(2)] public int? ANullableNonZeroDefaultInteger => 2; - [DefaultValue(2)] public int? ANullableNonZeroNonDefaultInteger => 1; - - // Enumerables - public int[] AnEmptyArray => new int[0]; - public IList AnEmptyList => new List(); - public Dictionary AnEmptyDictionary => new Dictionary(); - public IEnumerable AnEmptyEnumerable => Enumerable.Empty(); - - public string[] ANonEmptyArray => new[] { "foo", "bar" }; - public IList ANonEmptyList => new List { 6, 9, 42 }; - public IEnumerable ANonEmptyEnumerable => new[] { true, false }; - public Dictionary ANonEmptyDictionary => new Dictionary() { { "foo", "bar" } }; - } - - [Fact] - public void All_default_values_and_nulls_are_emitted_when_no_configuration_is_performed() - { - // Arrange - var sut = new SerializerBuilder() - .Build(); - - // Act - var yaml = sut.Serialize(new Model()); - - // Assert - Assert.Contains(nameof(Model.ANullString) + ':', yaml); - Assert.Contains(nameof(Model.ADefaultString) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultNullString) + ':', yaml); - - Assert.Contains(nameof(Model.AZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ADefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); - - Assert.Contains(nameof(Model.ANullInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); - } - - [Fact] - public void Only_null_values_are_omitted_when_DefaultValuesHandling_is_OmitNull() - { - // Arrange - var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) - .Build(); - - // Act - var yaml = sut.Serialize(new Model()); - - // Assert - Assert.DoesNotContain(nameof(Model.ANullString) + ':', yaml); - Assert.Contains(nameof(Model.ADefaultString) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); - Assert.DoesNotContain(nameof(Model.ANonDefaultNullString) + ':', yaml); - - Assert.Contains(nameof(Model.AZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ADefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); - - Assert.DoesNotContain(nameof(Model.ANullInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); - } - - [Fact] - public void All_default_values_are_omitted_when_DefaultValuesHandling_is_OmitAll() - { - // Arrange - var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) - .Build(); - - // Act - var yaml = sut.Serialize(new Model()); - - // Assert - Assert.DoesNotContain(nameof(Model.ANullString) + ':', yaml); - Assert.DoesNotContain(nameof(Model.ADefaultString) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultNullString) + ':', yaml); - - Assert.DoesNotContain(nameof(Model.AZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); - Assert.DoesNotContain(nameof(Model.ADefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); - - Assert.DoesNotContain(nameof(Model.ANullInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); - Assert.DoesNotContain(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); - } - - [Fact] - public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitEmpty() - { - // Arrange - var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitEmptyCollections) - .Build(); - - // Act - var yaml = sut.Serialize(new Model()); - - // Assert enumerables - Assert.DoesNotContain(nameof(Model.AnEmptyArray) + ':', yaml); - Assert.DoesNotContain(nameof(Model.AnEmptyList) + ':', yaml); - Assert.DoesNotContain(nameof(Model.AnEmptyDictionary) + ':', yaml); - Assert.DoesNotContain(nameof(Model.AnEmptyEnumerable) + ':', yaml); - - Assert.Contains(nameof(Model.ANonEmptyArray) + ':', yaml); - Assert.Contains(nameof(Model.ANonEmptyList) + ':', yaml); - Assert.Contains(nameof(Model.ANonEmptyDictionary) + ':', yaml); - Assert.Contains(nameof(Model.ANonEmptyEnumerable) + ':', yaml); - } - - [Fact] - public void YamlMember_overrides_default_value_handling() - { - // Arrange - var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) - .WithAttributeOverride(m => m.ANullString, new YamlMemberAttribute { DefaultValuesHandling = DefaultValuesHandling.Preserve }) - .Build(); - - // Act - var yaml = sut.Serialize(new Model()); - - // Assert - Assert.Contains(nameof(Model.ANullString) + ':', yaml); - Assert.DoesNotContain(nameof(Model.ANullInteger) + ':', yaml); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using Xunit; +using YamlDotNet.Serialization; + +namespace YamlDotNet.Test.Serialization +{ + public class EmitDefaultValuesTests + { + private class Model + { + public string ANullString => null; + [DefaultValue("hello")] public string ADefaultString => "hello"; + [DefaultValue("hello")] public string ANonDefaultString => "world"; + [DefaultValue("hello")] public string ANonDefaultNullString => null; + + public int AZeroInteger => 0; + public int ANonZeroInteger => 1; + [DefaultValue(2)] public int ADefaultInteger => 2; + [DefaultValue(2)] public int ANonDefaultZeroInteger => 0; + + public int? ANullInteger => null; + public int? ANullableZeroInteger => 0; + public int? ANullableNonZeroInteger => 1; + [DefaultValue(2)] public int? ANullableNonZeroDefaultInteger => 2; + [DefaultValue(2)] public int? ANullableNonZeroNonDefaultInteger => 1; + + // Enumerables + public int[] AnEmptyArray => new int[0]; + public IList AnEmptyList => new List(); + public Dictionary AnEmptyDictionary => new Dictionary(); + public IEnumerable AnEmptyEnumerable => Enumerable.Empty(); + + public string[] ANonEmptyArray => new[] { "foo", "bar" }; + public IList ANonEmptyList => new List { 6, 9, 42 }; + public IEnumerable ANonEmptyEnumerable => new[] { true, false }; + public Dictionary ANonEmptyDictionary => new Dictionary() { { "foo", "bar" } }; + } + + [Fact] + public void All_default_values_and_nulls_are_emitted_when_no_configuration_is_performed() + { + // Arrange + var sut = new SerializerBuilder() + .Build(); + + // Act + var yaml = sut.Serialize(new Model()); + + // Assert + Assert.Contains(nameof(Model.ANullString) + ':', yaml); + Assert.Contains(nameof(Model.ADefaultString) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultNullString) + ':', yaml); + + Assert.Contains(nameof(Model.AZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ADefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); + + Assert.Contains(nameof(Model.ANullInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); + } + + [Fact] + public void Only_null_values_are_omitted_when_DefaultValuesHandling_is_OmitNull() + { + // Arrange + var sut = new SerializerBuilder() + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) + .Build(); + + // Act + var yaml = sut.Serialize(new Model()); + + // Assert + Assert.DoesNotContain(nameof(Model.ANullString) + ':', yaml); + Assert.Contains(nameof(Model.ADefaultString) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); + Assert.DoesNotContain(nameof(Model.ANonDefaultNullString) + ':', yaml); + + Assert.Contains(nameof(Model.AZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ADefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); + + Assert.DoesNotContain(nameof(Model.ANullInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); + } + + [Fact] + public void All_default_values_are_omitted_when_DefaultValuesHandling_is_OmitAll() + { + // Arrange + var sut = new SerializerBuilder() + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) + .Build(); + + // Act + var yaml = sut.Serialize(new Model()); + + // Assert + Assert.DoesNotContain(nameof(Model.ANullString) + ':', yaml); + Assert.DoesNotContain(nameof(Model.ADefaultString) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultNullString) + ':', yaml); + + Assert.DoesNotContain(nameof(Model.AZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); + Assert.DoesNotContain(nameof(Model.ADefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); + + Assert.DoesNotContain(nameof(Model.ANullInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); + Assert.DoesNotContain(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); + } + + [Fact] + public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitEmpty() + { + // Arrange + var sut = new SerializerBuilder() + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitEmptyCollections) + .Build(); + + // Act + var yaml = sut.Serialize(new Model()); + + // Assert enumerables + Assert.DoesNotContain(nameof(Model.AnEmptyArray) + ':', yaml); + Assert.DoesNotContain(nameof(Model.AnEmptyList) + ':', yaml); + Assert.DoesNotContain(nameof(Model.AnEmptyDictionary) + ':', yaml); + Assert.DoesNotContain(nameof(Model.AnEmptyEnumerable) + ':', yaml); + + Assert.Contains(nameof(Model.ANonEmptyArray) + ':', yaml); + Assert.Contains(nameof(Model.ANonEmptyList) + ':', yaml); + Assert.Contains(nameof(Model.ANonEmptyDictionary) + ':', yaml); + Assert.Contains(nameof(Model.ANonEmptyEnumerable) + ':', yaml); + } + + [Fact] + public void YamlMember_overrides_default_value_handling() + { + // Arrange + var sut = new SerializerBuilder() + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) + .WithAttributeOverride(m => m.ANullString, new YamlMemberAttribute { DefaultValuesHandling = DefaultValuesHandling.Preserve }) + .Build(); + + // Act + var yaml = sut.Serialize(new Model()); + + // Assert + Assert.Contains(nameof(Model.ANullString) + ':', yaml); + Assert.DoesNotContain(nameof(Model.ANullInteger) + ':', yaml); + } + } +} diff --git a/YamlDotNet.Test/Serialization/GenericTestDictionary.cs b/YamlDotNet.Test/Serialization/GenericTestDictionary.cs index d98808f70..763074664 100644 --- a/YamlDotNet.Test/Serialization/GenericTestDictionary.cs +++ b/YamlDotNet.Test/Serialization/GenericTestDictionary.cs @@ -1,119 +1,119 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections; -using System.Collections.Generic; - -namespace YamlDotNet.Test.Serialization -{ - /// - /// Test Dictionary that implements , but not . - /// - public class GenericTestDictionary : IDictionary - { - private readonly Dictionary dictionary; - - public GenericTestDictionary() - { - dictionary = new Dictionary(); - } - public void Add(TKey key, TValue value) - { - dictionary.Add(key, value); - } - - public bool ContainsKey(TKey key) - { - return dictionary.ContainsKey(key); - } - - public ICollection Keys - { - get { return dictionary.Keys; } - } - - public bool Remove(TKey key) - { - return dictionary.Remove(key); - } - - public bool TryGetValue(TKey key, out TValue value) - { - return dictionary.TryGetValue(key, out value); - } - - public ICollection Values - { - get { return dictionary.Values; } - } - - public TValue this[TKey key] - { - get { return dictionary[key]; } - set { dictionary[key] = value; } - } - - public void Add(KeyValuePair item) - { - ((IDictionary)dictionary).Add(item); - } - - public void Clear() - { - dictionary.Clear(); - } - - public bool Contains(KeyValuePair item) - { - return ((IDictionary)dictionary).Contains(item); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - ((IDictionary)dictionary).CopyTo(array, arrayIndex); - } - - public int Count - { - get { return dictionary.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public bool Remove(KeyValuePair item) - { - return ((IDictionary)dictionary).Remove(item); - } - - public IEnumerator> GetEnumerator() - { - return dictionary.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return dictionary.GetEnumerator(); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections; +using System.Collections.Generic; + +namespace YamlDotNet.Test.Serialization +{ + /// + /// Test Dictionary that implements , but not . + /// + public class GenericTestDictionary : IDictionary + { + private readonly Dictionary dictionary; + + public GenericTestDictionary() + { + dictionary = new Dictionary(); + } + public void Add(TKey key, TValue value) + { + dictionary.Add(key, value); + } + + public bool ContainsKey(TKey key) + { + return dictionary.ContainsKey(key); + } + + public ICollection Keys + { + get { return dictionary.Keys; } + } + + public bool Remove(TKey key) + { + return dictionary.Remove(key); + } + + public bool TryGetValue(TKey key, out TValue value) + { + return dictionary.TryGetValue(key, out value); + } + + public ICollection Values + { + get { return dictionary.Values; } + } + + public TValue this[TKey key] + { + get { return dictionary[key]; } + set { dictionary[key] = value; } + } + + public void Add(KeyValuePair item) + { + ((IDictionary)dictionary).Add(item); + } + + public void Clear() + { + dictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return ((IDictionary)dictionary).Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)dictionary).CopyTo(array, arrayIndex); + } + + public int Count + { + get { return dictionary.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public bool Remove(KeyValuePair item) + { + return ((IDictionary)dictionary).Remove(item); + } + + public IEnumerator> GetEnumerator() + { + return dictionary.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return dictionary.GetEnumerator(); + } + } +} diff --git a/YamlDotNet.Test/Serialization/GenericTestList.cs b/YamlDotNet.Test/Serialization/GenericTestList.cs index d29b0b7a0..fdaa7f2f0 100644 --- a/YamlDotNet.Test/Serialization/GenericTestList.cs +++ b/YamlDotNet.Test/Serialization/GenericTestList.cs @@ -1,111 +1,111 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections; -using System.Collections.Generic; - -namespace YamlDotNet.Test.Serialization -{ - /// - /// Test List that implements , but not . - /// - public class GenericTestList : IList - { - private readonly List list; - - public GenericTestList() - { - list = new List(); - } - - public int IndexOf(T item) - { - return list.IndexOf(item); - } - - public void Insert(int index, T item) - { - list.Insert(index, item); - } - - public void RemoveAt(int index) - { - list.RemoveAt(index); - } - - public T this[int index] - { - get - { - return list[index]; - } - set - { - list[index] = value; - } - } - - public void Add(T item) - { - list.Add(item); - } - - public void Clear() - { - list.Clear(); - } - - public bool Contains(T item) - { - return list.Contains(item); - } - - public void CopyTo(T[] array, int arrayIndex) - { - list.CopyTo(array, arrayIndex); - } - - public int Count - { - get { return list.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public bool Remove(T item) - { - return list.Remove(item); - } - - public IEnumerator GetEnumerator() - { - return list.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return list.GetEnumerator(); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections; +using System.Collections.Generic; + +namespace YamlDotNet.Test.Serialization +{ + /// + /// Test List that implements , but not . + /// + public class GenericTestList : IList + { + private readonly List list; + + public GenericTestList() + { + list = new List(); + } + + public int IndexOf(T item) + { + return list.IndexOf(item); + } + + public void Insert(int index, T item) + { + list.Insert(index, item); + } + + public void RemoveAt(int index) + { + list.RemoveAt(index); + } + + public T this[int index] + { + get + { + return list[index]; + } + set + { + list[index] = value; + } + } + + public void Add(T item) + { + list.Add(item); + } + + public void Clear() + { + list.Clear(); + } + + public bool Contains(T item) + { + return list.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + list.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return list.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public bool Remove(T item) + { + return list.Remove(item); + } + + public IEnumerator GetEnumerator() + { + return list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return list.GetEnumerator(); + } + } +} diff --git a/YamlDotNet.Test/Serialization/NamingConventionTests.cs b/YamlDotNet.Test/Serialization/NamingConventionTests.cs index 681f5fdb6..9f22c60a1 100644 --- a/YamlDotNet.Test/Serialization/NamingConventionTests.cs +++ b/YamlDotNet.Test/Serialization/NamingConventionTests.cs @@ -1,85 +1,85 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using FluentAssertions; -using Xunit; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - -namespace YamlDotNet.Test.Serialization -{ - public class NamingConventionTests - { - [Theory] - [InlineData("test", "test")] - [InlineData("thisIsATest", "this-is-a-test")] - [InlineData("thisIsATest", "this_is_a_test")] - [InlineData("thisIsATest", "ThisIsATest")] - public void AppliesCamelCaseConvention(string expectedName, string input) - { - ShouldApplyConventionGiven(input, expectedName, CamelCaseNamingConvention.Instance); - } - - [Theory] - [InlineData("Test", "test")] - [InlineData("ThisIsATest", "this-is-a-test")] - [InlineData("ThisIsATest", "this_is_a_test")] - [InlineData("ThisIsATest", "thisIsATest")] - public void AppliesPascalCaseConvention(string expectedName, string input) - { - ShouldApplyConventionGiven(input, expectedName, PascalCaseNamingConvention.Instance); - } - - [Theory] - [InlineData("test", "test")] - [InlineData("this-is-a-test", "thisIsATest")] - [InlineData("this-is-a-test", "this-is-a-test")] - public void AppliesHyphenatedConvention(string expectedName, string input) - { - ShouldApplyConventionGiven(input, expectedName, HyphenatedNamingConvention.Instance); - } - - [Theory] - [InlineData("test", "test")] - [InlineData("this_is_a_test", "thisIsATest")] - [InlineData("this_is_a_test", "this-is-a-test")] - public void AppliesUnderscoredConvention(string expectedName, string input) - { - ShouldApplyConventionGiven(input, expectedName, UnderscoredNamingConvention.Instance); - } - - [Theory] - [InlineData("test", "test")] - [InlineData("thisisatest", "thisIsATest")] - [InlineData("thisisatest", "this-is-a-test")] - [InlineData("thisisatest", "this_is_a_test")] - [InlineData("thisisatest", "ThisIsATest")] - public void AppliesLowerCaseConvention(string expectedName, string input) - { - ShouldApplyConventionGiven(input, expectedName, LowerCaseNamingConvention.Instance); - } - - private void ShouldApplyConventionGiven(string input, string expectedName, INamingConvention convention) - { - convention.Apply(input).Should().Be(expectedName); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using FluentAssertions; +using Xunit; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace YamlDotNet.Test.Serialization +{ + public class NamingConventionTests + { + [Theory] + [InlineData("test", "test")] + [InlineData("thisIsATest", "this-is-a-test")] + [InlineData("thisIsATest", "this_is_a_test")] + [InlineData("thisIsATest", "ThisIsATest")] + public void AppliesCamelCaseConvention(string expectedName, string input) + { + ShouldApplyConventionGiven(input, expectedName, CamelCaseNamingConvention.Instance); + } + + [Theory] + [InlineData("Test", "test")] + [InlineData("ThisIsATest", "this-is-a-test")] + [InlineData("ThisIsATest", "this_is_a_test")] + [InlineData("ThisIsATest", "thisIsATest")] + public void AppliesPascalCaseConvention(string expectedName, string input) + { + ShouldApplyConventionGiven(input, expectedName, PascalCaseNamingConvention.Instance); + } + + [Theory] + [InlineData("test", "test")] + [InlineData("this-is-a-test", "thisIsATest")] + [InlineData("this-is-a-test", "this-is-a-test")] + public void AppliesHyphenatedConvention(string expectedName, string input) + { + ShouldApplyConventionGiven(input, expectedName, HyphenatedNamingConvention.Instance); + } + + [Theory] + [InlineData("test", "test")] + [InlineData("this_is_a_test", "thisIsATest")] + [InlineData("this_is_a_test", "this-is-a-test")] + public void AppliesUnderscoredConvention(string expectedName, string input) + { + ShouldApplyConventionGiven(input, expectedName, UnderscoredNamingConvention.Instance); + } + + [Theory] + [InlineData("test", "test")] + [InlineData("thisisatest", "thisIsATest")] + [InlineData("thisisatest", "this-is-a-test")] + [InlineData("thisisatest", "this_is_a_test")] + [InlineData("thisisatest", "ThisIsATest")] + public void AppliesLowerCaseConvention(string expectedName, string input) + { + ShouldApplyConventionGiven(input, expectedName, LowerCaseNamingConvention.Instance); + } + + private void ShouldApplyConventionGiven(string input, string expectedName, INamingConvention convention) + { + convention.Apply(input).Should().Be(expectedName); + } + } +} diff --git a/YamlDotNet.Test/Serialization/ObjectFactoryTests.cs b/YamlDotNet.Test/Serialization/ObjectFactoryTests.cs index fdbcfef72..fadca03fd 100644 --- a/YamlDotNet.Test/Serialization/ObjectFactoryTests.cs +++ b/YamlDotNet.Test/Serialization/ObjectFactoryTests.cs @@ -1,62 +1,62 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections; -using FluentAssertions; -using Xunit; -using YamlDotNet.Serialization.ObjectFactories; - -namespace YamlDotNet.Test.Serialization -{ - public class ObjectFactoryTests : SerializationTestHelper - { - [Fact] - public void NotSpecifyingObjectFactoryUsesDefault() - { - var text = "!empty {}"; - - DeserializerBuilder.WithTagMapping("!empty", typeof(EmptyBase)); - var result = Deserializer.Deserialize(UsingReaderFor(text)); - - result.Should().BeOfType(); - } - - [Fact] - public void ObjectFactoryIsInvoked() - { - AssumingDeserializerWith(new LambdaObjectFactory(t => new EmptyDerived())); - var text = "!empty {}"; - - DeserializerBuilder.WithTagMapping("!empty", typeof(EmptyBase)); - var result = Deserializer.Deserialize(UsingReaderFor(text)); - - result.Should().BeOfType(); - } - - [Fact] - public void DefaultObjectFactorySupportsNonGenericInterfaces() - { - var sut = new DefaultObjectFactory(); - var result = sut.Create(typeof(IList)); - Assert.IsAssignableFrom(result); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections; +using FluentAssertions; +using Xunit; +using YamlDotNet.Serialization.ObjectFactories; + +namespace YamlDotNet.Test.Serialization +{ + public class ObjectFactoryTests : SerializationTestHelper + { + [Fact] + public void NotSpecifyingObjectFactoryUsesDefault() + { + var text = "!empty {}"; + + DeserializerBuilder.WithTagMapping("!empty", typeof(EmptyBase)); + var result = Deserializer.Deserialize(UsingReaderFor(text)); + + result.Should().BeOfType(); + } + + [Fact] + public void ObjectFactoryIsInvoked() + { + AssumingDeserializerWith(new LambdaObjectFactory(t => new EmptyDerived())); + var text = "!empty {}"; + + DeserializerBuilder.WithTagMapping("!empty", typeof(EmptyBase)); + var result = Deserializer.Deserialize(UsingReaderFor(text)); + + result.Should().BeOfType(); + } + + [Fact] + public void DefaultObjectFactorySupportsNonGenericInterfaces() + { + var sut = new DefaultObjectFactory(); + var result = sut.Create(typeof(IList)); + Assert.IsAssignableFrom(result); + } + } +} diff --git a/YamlDotNet.Test/Serialization/RepresentationModelSerializationTests.cs b/YamlDotNet.Test/Serialization/RepresentationModelSerializationTests.cs index 1ef73aaca..7bd567b56 100644 --- a/YamlDotNet.Test/Serialization/RepresentationModelSerializationTests.cs +++ b/YamlDotNet.Test/Serialization/RepresentationModelSerializationTests.cs @@ -1,129 +1,129 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using Xunit; -using YamlDotNet.Core; -using YamlDotNet.RepresentationModel; -using YamlDotNet.Serialization; - -namespace YamlDotNet.Test.Serialization -{ - public class RepresentationModelSerializationTests - { - [Theory] - [InlineData("hello", "hello")] - [InlineData("'hello'", "hello")] - [InlineData("\"hello\"", "hello")] - [InlineData("!!int 123", "123")] - public void ScalarIsSerializable(string yaml, string expectedValue) - { - var deserializer = new Deserializer(); - var node = deserializer.Deserialize(Yaml.ReaderForText(yaml)); - - Assert.NotNull(node); - Assert.Equal(expectedValue, node.Value); - - var serializer = new SerializerBuilder() - .Build(); - var buffer = new StringWriter(); - serializer.Serialize(buffer, node); - Assert.Equal(yaml, buffer.ToString().TrimEnd('\r', '\n', '.')); - } - - [Theory] - [InlineData("[a]", new[] { "a" })] - [InlineData("['a']", new[] { "a" })] - [InlineData("- a\r\n- b", new[] { "a", "b" })] - [InlineData("!bla [a]", new[] { "a" })] - public void SequenceIsSerializable(string yaml, string[] expectedValues) - { - var deserializer = new Deserializer(); - var node = deserializer.Deserialize(Yaml.ReaderForText(yaml)); - - Assert.NotNull(node); - Assert.Equal(expectedValues.Length, node.Children.Count); - for (var i = 0; i < expectedValues.Length; i++) - { - Assert.Equal(expectedValues[i], ((YamlScalarNode)node.Children[i]).Value); - } - - var serializer = new SerializerBuilder() - .Build(); - var buffer = new StringWriter(); - serializer.Serialize(buffer, node); - Assert.Equal(yaml.NormalizeNewLines(), buffer.ToString().TrimEnd('\r', '\n', '.')); - } - - [Theory] - [InlineData("{a: b}", new[] { "a", "b" })] - [InlineData("{'a': \"b\"}", new[] { "a", "b" })] - [InlineData("a: b\r\nc: d", new[] { "a", "b", "c", "d" })] - public void MappingIsSerializable(string yaml, string[] expectedKeysAndValues) - { - var deserializer = new Deserializer(); - var node = deserializer.Deserialize(Yaml.ReaderForText(yaml)); - - Assert.NotNull(node); - Assert.Equal(expectedKeysAndValues.Length / 2, node.Children.Count); - for (var i = 0; i < expectedKeysAndValues.Length; i += 2) - { - Assert.Equal(expectedKeysAndValues[i + 1], ((YamlScalarNode)node.Children[expectedKeysAndValues[i]]).Value); - } - - var serializer = new SerializerBuilder() - .Build(); - var buffer = new StringWriter(); - serializer.Serialize(buffer, node); - Assert.Equal(yaml.NormalizeNewLines(), buffer.ToString().TrimEnd('\r', '\n', '.')); - } - } - - public class ByteArrayConverter : IYamlTypeConverter - { - public bool Accepts(Type type) - { - return type == typeof(byte[]); - } - - public object ReadYaml(IParser parser, Type type) - { - var scalar = (YamlDotNet.Core.Events.Scalar)parser.Current; - var bytes = Convert.FromBase64String(scalar.Value); - parser.MoveNext(); - return bytes; - } - - public void WriteYaml(IEmitter emitter, object value, Type type) - { - var bytes = (byte[])value; - emitter.Emit(new YamlDotNet.Core.Events.Scalar( - AnchorName.Empty, - "tag:yaml.org,2002:binary", - Convert.ToBase64String(bytes), - ScalarStyle.Plain, - false, - false - )); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.IO; +using Xunit; +using YamlDotNet.Core; +using YamlDotNet.RepresentationModel; +using YamlDotNet.Serialization; + +namespace YamlDotNet.Test.Serialization +{ + public class RepresentationModelSerializationTests + { + [Theory] + [InlineData("hello", "hello")] + [InlineData("'hello'", "hello")] + [InlineData("\"hello\"", "hello")] + [InlineData("!!int 123", "123")] + public void ScalarIsSerializable(string yaml, string expectedValue) + { + var deserializer = new Deserializer(); + var node = deserializer.Deserialize(Yaml.ReaderForText(yaml)); + + Assert.NotNull(node); + Assert.Equal(expectedValue, node.Value); + + var serializer = new SerializerBuilder() + .Build(); + var buffer = new StringWriter(); + serializer.Serialize(buffer, node); + Assert.Equal(yaml, buffer.ToString().TrimEnd('\r', '\n', '.')); + } + + [Theory] + [InlineData("[a]", new[] { "a" })] + [InlineData("['a']", new[] { "a" })] + [InlineData("- a\r\n- b", new[] { "a", "b" })] + [InlineData("!bla [a]", new[] { "a" })] + public void SequenceIsSerializable(string yaml, string[] expectedValues) + { + var deserializer = new Deserializer(); + var node = deserializer.Deserialize(Yaml.ReaderForText(yaml)); + + Assert.NotNull(node); + Assert.Equal(expectedValues.Length, node.Children.Count); + for (var i = 0; i < expectedValues.Length; i++) + { + Assert.Equal(expectedValues[i], ((YamlScalarNode)node.Children[i]).Value); + } + + var serializer = new SerializerBuilder() + .Build(); + var buffer = new StringWriter(); + serializer.Serialize(buffer, node); + Assert.Equal(yaml.NormalizeNewLines(), buffer.ToString().TrimEnd('\r', '\n', '.')); + } + + [Theory] + [InlineData("{a: b}", new[] { "a", "b" })] + [InlineData("{'a': \"b\"}", new[] { "a", "b" })] + [InlineData("a: b\r\nc: d", new[] { "a", "b", "c", "d" })] + public void MappingIsSerializable(string yaml, string[] expectedKeysAndValues) + { + var deserializer = new Deserializer(); + var node = deserializer.Deserialize(Yaml.ReaderForText(yaml)); + + Assert.NotNull(node); + Assert.Equal(expectedKeysAndValues.Length / 2, node.Children.Count); + for (var i = 0; i < expectedKeysAndValues.Length; i += 2) + { + Assert.Equal(expectedKeysAndValues[i + 1], ((YamlScalarNode)node.Children[expectedKeysAndValues[i]]).Value); + } + + var serializer = new SerializerBuilder() + .Build(); + var buffer = new StringWriter(); + serializer.Serialize(buffer, node); + Assert.Equal(yaml.NormalizeNewLines(), buffer.ToString().TrimEnd('\r', '\n', '.')); + } + } + + public class ByteArrayConverter : IYamlTypeConverter + { + public bool Accepts(Type type) + { + return type == typeof(byte[]); + } + + public object ReadYaml(IParser parser, Type type) + { + var scalar = (YamlDotNet.Core.Events.Scalar)parser.Current; + var bytes = Convert.FromBase64String(scalar.Value); + parser.MoveNext(); + return bytes; + } + + public void WriteYaml(IEmitter emitter, object value, Type type) + { + var bytes = (byte[])value; + emitter.Emit(new YamlDotNet.Core.Events.Scalar( + AnchorName.Empty, + "tag:yaml.org,2002:binary", + Convert.ToBase64String(bytes), + ScalarStyle.Plain, + false, + false + )); + } + } +} diff --git a/YamlDotNet.Test/Serialization/SerializationTestHelper.cs b/YamlDotNet.Test/Serialization/SerializationTestHelper.cs index e5c7cc061..d11c22d01 100644 --- a/YamlDotNet.Test/Serialization/SerializationTestHelper.cs +++ b/YamlDotNet.Test/Serialization/SerializationTestHelper.cs @@ -1,599 +1,599 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Globalization; -using System.IO; -using FluentAssertions; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; - -namespace YamlDotNet.Test.Serialization -{ - public class SerializationTestHelper - { - private SerializerBuilder serializerBuilder; - private DeserializerBuilder deserializerBuilder; - - protected T DoRoundtripFromObjectTo(object obj) - { - return DoRoundtripFromObjectTo(obj, Serializer); - } - - protected T DoRoundtripFromObjectTo(object obj, ISerializer serializer) - { - return DoRoundtripFromObjectTo(obj, serializer, Deserializer); - } - - protected T DoRoundtripFromObjectTo(object obj, ISerializer serializer, IDeserializer deserializer) - { - var writer = new StringWriter(); - serializer.Serialize(writer, obj); - return deserializer.Deserialize(UsingReaderFor(writer)); - } - - protected T DoRoundtripOn(object obj) - { - return DoRoundtripOn(obj, Serializer); - } - - protected T DoRoundtripOn(object obj, ISerializer serializer) - { - var writer = new StringWriter(); - serializer.Serialize(writer, obj, typeof(T)); - return new Deserializer().Deserialize(UsingReaderFor(writer)); - } - - protected SerializerBuilder SerializerBuilder - { - get - { - return serializerBuilder ??= new SerializerBuilder(); - } - } - - protected ISerializer Serializer - { - get - { - return SerializerBuilder.Build(); - } - } - - protected DeserializerBuilder DeserializerBuilder - { - get - { - return deserializerBuilder ??= new DeserializerBuilder(); - } - } - - protected IDeserializer Deserializer - { - get - { - return DeserializerBuilder.Build(); - } - } - - protected void AssumingDeserializerWith(IObjectFactory factory) - { - deserializerBuilder = new DeserializerBuilder() - .WithObjectFactory(factory); - } - - protected TextReader UsingReaderFor(TextWriter buffer) - { - return UsingReaderFor(buffer.ToString()); - } - - protected TextReader UsingReaderFor(string text) - { - return new StringReader(text); - } - - protected static IParser ParserFor(string yaml) - { - return new Parser(new StringReader(yaml)); - } - - protected string Lines(params string[] lines) - { - return string.Join("\r\n", lines); - } - - protected object Entry(string key, string value) - { - return new DictionaryEntry(key, value); - } - } - - // ReSharper disable InconsistentNaming - - [Flags] - public enum EnumExample - { - None, - One, - Two - } - - public struct StructExample - { - public int Value { get; set; } - } - - public enum SByteEnum : sbyte { Default, Sbyte } - public enum ByteEnum : byte { Default, Byte } - public enum Int16Enum : short { Default, Short } - public enum UInt16Enum : ushort { Default, Ushort } - public enum Int32Enum : int { Default, Int } - public enum UInt32Enum : uint { Default, Uint } - public enum Int64Enum : long { Default, Long } - public enum UInt64Enum : ulong { Default, Ulong } - - public class CircularReference - { - public CircularReference Child1 { get; set; } - public CircularReference Child2 { get; set; } - } - - [TypeConverter(typeof(ConvertibleConverter))] - public class Convertible : IConvertible - { - public string Left { get; set; } - public string Right { get; set; } - - public object ToType(Type conversionType, IFormatProvider provider) - { - conversionType.Should().Be(); - return ToString(provider); - } - - public string ToString(IFormatProvider provider) - { - provider.Should().Be(CultureInfo.InvariantCulture); - return string.Format(provider, "[{0}, {1}]", Left, Right); - } - - #region Unsupported Members - - public System.TypeCode GetTypeCode() - { - throw new NotSupportedException(); - } - - public bool ToBoolean(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public byte ToByte(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public char ToChar(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public DateTime ToDateTime(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public decimal ToDecimal(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public double ToDouble(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public short ToInt16(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public int ToInt32(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public long ToInt64(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public sbyte ToSByte(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public float ToSingle(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public ushort ToUInt16(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public uint ToUInt32(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - public ulong ToUInt64(IFormatProvider provider) - { - throw new NotSupportedException(); - } - - #endregion - } - - public class ConvertibleConverter : TypeConverter - { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - return sourceType == typeof(string); - } - - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - return false; - } - - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) - { - if (!(value is string)) - { - throw new InvalidOperationException(); - } - - var parts = (value as string).Split(' '); - return new Convertible - { - Left = parts[0], - Right = parts[1] - }; - } - } - - public class MissingDefaultCtor - { - public string Value; - - public MissingDefaultCtor(string value) - { - Value = value; - } - } - - public class MissingDefaultCtorConverter : IYamlTypeConverter - { - public bool Accepts(Type type) - { - return type == typeof(MissingDefaultCtor); - } - - public object ReadYaml(IParser parser, Type type) - { - var value = ((Scalar)parser.Current).Value; - parser.MoveNext(); - return new MissingDefaultCtor(value); - } - - public void WriteYaml(IEmitter emitter, object value, Type type) - { - emitter.Emit(new Scalar(((MissingDefaultCtor)value).Value)); - } - } - - public class InheritanceExample - { - public object SomeScalar { get; set; } - public Base RegularBase { get; set; } - - [YamlMember(serializeAs: typeof(Base))] - public Base BaseWithSerializeAs { get; set; } - } - - public class InterfaceExample - { - public IDerived Derived { get; set; } - } - - public interface IBase - { - string BaseProperty { get; set; } - } - - public interface IDerived : IBase - { - string DerivedProperty { get; set; } - } - - public class Base : IBase - { - public string BaseProperty { get; set; } - } - - public class Derived : Base, IDerived - { - public string DerivedProperty { get; set; } - } - - public class EmptyBase - { - } - - public class EmptyDerived : EmptyBase - { - } - - public class Simple - { - public string aaa { get; set; } - } - - public class SimpleScratch - { - public string Scratch { get; set; } - public bool DeleteScratch { get; set; } - public IEnumerable MappedScratch { get; set; } - } - - public class Example - { - public bool MyFlag { get; set; } - public string Nothing { get; set; } - public int MyInt { get; set; } - public double MyDouble { get; set; } - public string MyString { get; set; } - public DateTime MyDate { get; set; } - public TimeSpan MyTimeSpan { get; set; } - public Point MyPoint { get; set; } - public int? MyNullableWithValue { get; set; } - public int? MyNullableWithoutValue { get; set; } - - public Example() - { - MyInt = 1234; - MyDouble = 6789.1011; - MyString = "Hello world"; - MyDate = DateTime.Now; - MyTimeSpan = TimeSpan.FromHours(1); - MyPoint = new Point(100, 200); - MyNullableWithValue = 8; - } - } - - public class OrderExample - { - public OrderExample() - { - this.Order1 = "Order1 value"; - this.Order2 = "Order2 value"; - } - - [YamlMember(Order = 2)] - public string Order2 { get; set; } - - [YamlMember(Order = 1)] - public string Order1 { get; set; } - } - - public class IgnoreExample - { - [YamlIgnore] - public string IgnoreMe - { - get { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } - set { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } - } - } - - public class IgnoreExampleBase - { - [YamlIgnore] - public virtual string IgnoreMe - { - get { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } - set { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } - } - } - - public class IgnoreExampleDerived : IgnoreExampleBase - { - public override string IgnoreMe - { - get { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } - set { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } - } - } - - public class ScalarStyleExample - { - public ScalarStyleExample() - { - var content = "Test"; - LiteralString = content; - DoubleQuotedString = content; - } - - [YamlMember(ScalarStyle = ScalarStyle.Literal)] - public string LiteralString { get; set; } - - [YamlMember(ScalarStyle = ScalarStyle.DoubleQuoted)] - public string DoubleQuotedString { get; set; } - } - - public class DefaultsExample - { - public const string DefaultValue = "myDefault"; - - [DefaultValue(DefaultValue)] - public string Value { get; set; } - } - - public class CustomGenericDictionary : IDictionary - { - private readonly Dictionary dictionary = new Dictionary(); - - public void Add(string key, string value) - { - dictionary.Add(key, value); - } - - public IEnumerator> GetEnumerator() - { - return dictionary.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #region Unsupported Members - - public bool ContainsKey(string key) - { - throw new NotSupportedException(); - } - - public ICollection Keys - { - get { throw new NotSupportedException(); } - } - - public bool Remove(string key) - { - throw new NotSupportedException(); - } - - public bool TryGetValue(string key, out string value) - { - throw new NotSupportedException(); - } - - public ICollection Values - { - get { throw new NotSupportedException(); } - } - - public string this[string key] - { - get { throw new NotSupportedException(); } - set { throw new NotSupportedException(); } - } - - public void Add(KeyValuePair item) - { - throw new NotSupportedException(); - } - - public void Clear() - { - throw new NotSupportedException(); - } - - public bool Contains(KeyValuePair item) - { - throw new NotSupportedException(); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - throw new NotSupportedException(); - } - - public int Count - { - get { throw new NotSupportedException(); } - } - - public bool IsReadOnly - { - get { throw new NotSupportedException(); } - } - - public bool Remove(KeyValuePair item) - { - throw new NotSupportedException(); - } - - #endregion - } - - public class NameConvention - { - public string FirstTest { get; set; } - public string SecondTest { get; set; } - public string ThirdTest { get; set; } - - [YamlMember(Alias = "fourthTest")] - public string AliasTest { get; set; } - - [YamlIgnore] - public string fourthTest { get; set; } - } - - public class NonPublicPropertiesExample - { - public string Public { get; set; } = "public"; - - internal string Internal { get; set; } = "internal"; - - protected string Protected { get; set; } = "protected"; - - private string Private { get; set; } = "private"; - - /// - public override string ToString() => $"{Public},{Internal},{Protected},{Private}"; - } - -#pragma warning disable IDE0044 // Add readonly modifier - public class NonPublicFieldsExample - { - public string Public = "public"; - - internal string Internal = "internal"; - - protected string Protected = "protected"; - - private string Private = "private"; - - /// - public override string ToString() => $"{Public},{Internal},{Protected},{Private}"; - } -#pragma warning restore IDE0044 // Add readonly modifier -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Globalization; +using System.IO; +using FluentAssertions; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace YamlDotNet.Test.Serialization +{ + public class SerializationTestHelper + { + private SerializerBuilder serializerBuilder; + private DeserializerBuilder deserializerBuilder; + + protected T DoRoundtripFromObjectTo(object obj) + { + return DoRoundtripFromObjectTo(obj, Serializer); + } + + protected T DoRoundtripFromObjectTo(object obj, ISerializer serializer) + { + return DoRoundtripFromObjectTo(obj, serializer, Deserializer); + } + + protected T DoRoundtripFromObjectTo(object obj, ISerializer serializer, IDeserializer deserializer) + { + var writer = new StringWriter(); + serializer.Serialize(writer, obj); + return deserializer.Deserialize(UsingReaderFor(writer)); + } + + protected T DoRoundtripOn(object obj) + { + return DoRoundtripOn(obj, Serializer); + } + + protected T DoRoundtripOn(object obj, ISerializer serializer) + { + var writer = new StringWriter(); + serializer.Serialize(writer, obj, typeof(T)); + return new Deserializer().Deserialize(UsingReaderFor(writer)); + } + + protected SerializerBuilder SerializerBuilder + { + get + { + return serializerBuilder ??= new SerializerBuilder(); + } + } + + protected ISerializer Serializer + { + get + { + return SerializerBuilder.Build(); + } + } + + protected DeserializerBuilder DeserializerBuilder + { + get + { + return deserializerBuilder ??= new DeserializerBuilder(); + } + } + + protected IDeserializer Deserializer + { + get + { + return DeserializerBuilder.Build(); + } + } + + protected void AssumingDeserializerWith(IObjectFactory factory) + { + deserializerBuilder = new DeserializerBuilder() + .WithObjectFactory(factory); + } + + protected TextReader UsingReaderFor(TextWriter buffer) + { + return UsingReaderFor(buffer.ToString()); + } + + protected TextReader UsingReaderFor(string text) + { + return new StringReader(text); + } + + protected static IParser ParserFor(string yaml) + { + return new Parser(new StringReader(yaml)); + } + + protected string Lines(params string[] lines) + { + return string.Join("\r\n", lines); + } + + protected object Entry(string key, string value) + { + return new DictionaryEntry(key, value); + } + } + + // ReSharper disable InconsistentNaming + + [Flags] + public enum EnumExample + { + None, + One, + Two + } + + public struct StructExample + { + public int Value { get; set; } + } + + public enum SByteEnum : sbyte { Default, Sbyte } + public enum ByteEnum : byte { Default, Byte } + public enum Int16Enum : short { Default, Short } + public enum UInt16Enum : ushort { Default, Ushort } + public enum Int32Enum : int { Default, Int } + public enum UInt32Enum : uint { Default, Uint } + public enum Int64Enum : long { Default, Long } + public enum UInt64Enum : ulong { Default, Ulong } + + public class CircularReference + { + public CircularReference Child1 { get; set; } + public CircularReference Child2 { get; set; } + } + + [TypeConverter(typeof(ConvertibleConverter))] + public class Convertible : IConvertible + { + public string Left { get; set; } + public string Right { get; set; } + + public object ToType(Type conversionType, IFormatProvider provider) + { + conversionType.Should().Be(); + return ToString(provider); + } + + public string ToString(IFormatProvider provider) + { + provider.Should().Be(CultureInfo.InvariantCulture); + return string.Format(provider, "[{0}, {1}]", Left, Right); + } + + #region Unsupported Members + + public System.TypeCode GetTypeCode() + { + throw new NotSupportedException(); + } + + public bool ToBoolean(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public byte ToByte(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public char ToChar(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public DateTime ToDateTime(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public decimal ToDecimal(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public double ToDouble(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public short ToInt16(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public int ToInt32(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public long ToInt64(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public sbyte ToSByte(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public float ToSingle(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public ushort ToUInt16(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public uint ToUInt32(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + public ulong ToUInt64(IFormatProvider provider) + { + throw new NotSupportedException(); + } + + #endregion + } + + public class ConvertibleConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return false; + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (!(value is string)) + { + throw new InvalidOperationException(); + } + + var parts = (value as string).Split(' '); + return new Convertible + { + Left = parts[0], + Right = parts[1] + }; + } + } + + public class MissingDefaultCtor + { + public string Value; + + public MissingDefaultCtor(string value) + { + Value = value; + } + } + + public class MissingDefaultCtorConverter : IYamlTypeConverter + { + public bool Accepts(Type type) + { + return type == typeof(MissingDefaultCtor); + } + + public object ReadYaml(IParser parser, Type type) + { + var value = ((Scalar)parser.Current).Value; + parser.MoveNext(); + return new MissingDefaultCtor(value); + } + + public void WriteYaml(IEmitter emitter, object value, Type type) + { + emitter.Emit(new Scalar(((MissingDefaultCtor)value).Value)); + } + } + + public class InheritanceExample + { + public object SomeScalar { get; set; } + public Base RegularBase { get; set; } + + [YamlMember(serializeAs: typeof(Base))] + public Base BaseWithSerializeAs { get; set; } + } + + public class InterfaceExample + { + public IDerived Derived { get; set; } + } + + public interface IBase + { + string BaseProperty { get; set; } + } + + public interface IDerived : IBase + { + string DerivedProperty { get; set; } + } + + public class Base : IBase + { + public string BaseProperty { get; set; } + } + + public class Derived : Base, IDerived + { + public string DerivedProperty { get; set; } + } + + public class EmptyBase + { + } + + public class EmptyDerived : EmptyBase + { + } + + public class Simple + { + public string aaa { get; set; } + } + + public class SimpleScratch + { + public string Scratch { get; set; } + public bool DeleteScratch { get; set; } + public IEnumerable MappedScratch { get; set; } + } + + public class Example + { + public bool MyFlag { get; set; } + public string Nothing { get; set; } + public int MyInt { get; set; } + public double MyDouble { get; set; } + public string MyString { get; set; } + public DateTime MyDate { get; set; } + public TimeSpan MyTimeSpan { get; set; } + public Point MyPoint { get; set; } + public int? MyNullableWithValue { get; set; } + public int? MyNullableWithoutValue { get; set; } + + public Example() + { + MyInt = 1234; + MyDouble = 6789.1011; + MyString = "Hello world"; + MyDate = DateTime.Now; + MyTimeSpan = TimeSpan.FromHours(1); + MyPoint = new Point(100, 200); + MyNullableWithValue = 8; + } + } + + public class OrderExample + { + public OrderExample() + { + this.Order1 = "Order1 value"; + this.Order2 = "Order2 value"; + } + + [YamlMember(Order = 2)] + public string Order2 { get; set; } + + [YamlMember(Order = 1)] + public string Order1 { get; set; } + } + + public class IgnoreExample + { + [YamlIgnore] + public string IgnoreMe + { + get { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } + set { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } + } + } + + public class IgnoreExampleBase + { + [YamlIgnore] + public virtual string IgnoreMe + { + get { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } + set { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } + } + } + + public class IgnoreExampleDerived : IgnoreExampleBase + { + public override string IgnoreMe + { + get { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } + set { throw new InvalidOperationException("Accessing a [YamlIgnore] property"); } + } + } + + public class ScalarStyleExample + { + public ScalarStyleExample() + { + var content = "Test"; + LiteralString = content; + DoubleQuotedString = content; + } + + [YamlMember(ScalarStyle = ScalarStyle.Literal)] + public string LiteralString { get; set; } + + [YamlMember(ScalarStyle = ScalarStyle.DoubleQuoted)] + public string DoubleQuotedString { get; set; } + } + + public class DefaultsExample + { + public const string DefaultValue = "myDefault"; + + [DefaultValue(DefaultValue)] + public string Value { get; set; } + } + + public class CustomGenericDictionary : IDictionary + { + private readonly Dictionary dictionary = new Dictionary(); + + public void Add(string key, string value) + { + dictionary.Add(key, value); + } + + public IEnumerator> GetEnumerator() + { + return dictionary.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #region Unsupported Members + + public bool ContainsKey(string key) + { + throw new NotSupportedException(); + } + + public ICollection Keys + { + get { throw new NotSupportedException(); } + } + + public bool Remove(string key) + { + throw new NotSupportedException(); + } + + public bool TryGetValue(string key, out string value) + { + throw new NotSupportedException(); + } + + public ICollection Values + { + get { throw new NotSupportedException(); } + } + + public string this[string key] + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public void Add(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotSupportedException(); + } + + public int Count + { + get { throw new NotSupportedException(); } + } + + public bool IsReadOnly + { + get { throw new NotSupportedException(); } + } + + public bool Remove(KeyValuePair item) + { + throw new NotSupportedException(); + } + + #endregion + } + + public class NameConvention + { + public string FirstTest { get; set; } + public string SecondTest { get; set; } + public string ThirdTest { get; set; } + + [YamlMember(Alias = "fourthTest")] + public string AliasTest { get; set; } + + [YamlIgnore] + public string fourthTest { get; set; } + } + + public class NonPublicPropertiesExample + { + public string Public { get; set; } = "public"; + + internal string Internal { get; set; } = "internal"; + + protected string Protected { get; set; } = "protected"; + + private string Private { get; set; } = "private"; + + /// + public override string ToString() => $"{Public},{Internal},{Protected},{Private}"; + } + +#pragma warning disable IDE0044 // Add readonly modifier + public class NonPublicFieldsExample + { + public string Public = "public"; + + internal string Internal = "internal"; + + protected string Protected = "protected"; + + private string Private = "private"; + + /// + public override string ToString() => $"{Public},{Internal},{Protected},{Private}"; + } +#pragma warning restore IDE0044 // Add readonly modifier +} diff --git a/YamlDotNet.Test/Serialization/SerializationTests.cs b/YamlDotNet.Test/Serialization/SerializationTests.cs index be26f2b90..f6855d0dd 100644 --- a/YamlDotNet.Test/Serialization/SerializationTests.cs +++ b/YamlDotNet.Test/Serialization/SerializationTests.cs @@ -1,2335 +1,2335 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Dynamic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using FakeItEasy; -using FluentAssertions; -using Xunit; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; -using YamlDotNet.Serialization.ObjectFactories; - -namespace YamlDotNet.Test.Serialization -{ - public class SerializationTests : SerializationTestHelper - { - #region Test Cases - - private static readonly string[] TrueStrings = { "true", "y", "yes", "on" }; - private static readonly string[] FalseStrings = { "false", "n", "no", "off" }; - - public static IEnumerable DeserializeScalarBoolean_TestCases - { - get - { - foreach (var trueString in TrueStrings) - { - yield return new object[] { trueString, true }; - yield return new object[] { trueString.ToUpper(), true }; - } - - foreach (var falseString in FalseStrings) - { - yield return new object[] { falseString, false }; - yield return new object[] { falseString.ToUpper(), false }; - } - } - } - - #endregion - - [Fact] - public void DeserializeEmptyDocument() - { - var emptyText = string.Empty; - - var array = Deserializer.Deserialize(UsingReaderFor(emptyText)); - - array.Should().BeNull(); - } - - [Fact] - public void DeserializeScalar() - { - var stream = Yaml.ReaderFrom("02-scalar-in-imp-doc.yaml"); - - var result = Deserializer.Deserialize(stream); - - result.Should().Be("a scalar"); - } - - [Theory] - [MemberData(nameof(DeserializeScalarBoolean_TestCases))] - public void DeserializeScalarBoolean(string value, bool expected) - { - var result = Deserializer.Deserialize(UsingReaderFor(value)); - - result.Should().Be(expected); - } - - [Fact] - public void DeserializeScalarBooleanThrowsWhenInvalid() - { - Action action = () => Deserializer.Deserialize(UsingReaderFor("not-a-boolean")); - - action.ShouldThrow().WithInnerException(); - } - - [Fact] - public void DeserializeScalarZero() - { - var result = Deserializer.Deserialize(UsingReaderFor("0")); - - result.Should().Be(0); - } - - [Fact] - public void DeserializeScalarDecimal() - { - var result = Deserializer.Deserialize(UsingReaderFor("+1_234_567")); - - result.Should().Be(1234567); - } - - [Fact] - public void DeserializeScalarBinaryNumber() - { - var result = Deserializer.Deserialize(UsingReaderFor("-0b1_0010_1001_0010")); - - result.Should().Be(-4754); - } - - [Fact] - public void DeserializeScalarOctalNumber() - { - var result = Deserializer.Deserialize(UsingReaderFor("+071_352")); - - result.Should().Be(29418); - } - - [Fact] - public void DeserializeNullableScalarOctalNumber() - { - var result = Deserializer.Deserialize(UsingReaderFor("+071_352")); - - result.Should().Be(29418); - } - - [Fact] - public void DeserializeScalarHexNumber() - { - var result = Deserializer.Deserialize(UsingReaderFor("-0x_0F_B9")); - - result.Should().Be(-0xFB9); - } - - [Fact] - public void DeserializeScalarLongBase60Number() - { - var result = Deserializer.Deserialize(UsingReaderFor("99_:_58:47:3:6_2:10")); - - result.Should().Be(77744246530L); - } - - [Theory] - [InlineData(EnumExample.One)] - [InlineData(EnumExample.One | EnumExample.Two)] - public void RoundtripEnums(EnumExample value) - { - var result = DoRoundtripFromObjectTo(value); - - result.Should().Be(value); - } - - [Theory] - [InlineData(EnumExample.One)] - [InlineData(EnumExample.One | EnumExample.Two)] - [InlineData(null)] - public void RoundtripNullableEnums(EnumExample? value) - { - var result = DoRoundtripFromObjectTo(value); - - result.Should().Be(value); - } - - [Fact] - public void RoundtripNullableStructWithValue() - { - var value = new StructExample { Value = 2 }; - - var result = DoRoundtripFromObjectTo(value); - - result.Should().Be(value); - } - - [Fact] - public void RoundtripNullableStructWithoutValue() - { - var result = DoRoundtripFromObjectTo(null); - - result.Should().Be(null); - } - - [Fact] - public void SerializeCircularReference() - { - var obj = new CircularReference(); - obj.Child1 = new CircularReference - { - Child1 = obj, - Child2 = obj - }; - - Action action = () => SerializerBuilder.EnsureRoundtrip().Build().Serialize(new StringWriter(), obj, typeof(CircularReference)); - - action.ShouldNotThrow(); - } - - [Fact] - public void DeserializeIncompleteDirective() - { - Action action = () => Deserializer.Deserialize(UsingReaderFor("%Y")); - - action.ShouldThrow() - .WithMessage("While scanning a directive, found unexpected end of stream."); - } - - [Fact] - public void DeserializeSkippedReservedDirective() - { - Action action = () => Deserializer.Deserialize(UsingReaderFor("%Y ")); - - action.ShouldNotThrow(); - } - - [Fact] - public void DeserializeCustomTags() - { - var stream = Yaml.ReaderFrom("tags.yaml"); - - DeserializerBuilder.WithTagMapping("tag:yaml.org,2002:point", typeof(Point)); - var result = Deserializer.Deserialize(stream); - - result.Should().BeOfType().And - .Subject.As() - .ShouldBeEquivalentTo(new { X = 10, Y = 20 }, o => o.ExcludingMissingMembers()); - } - - [Fact] - public void DeserializeWithGapsBetweenKeys() - { - var yamlReader = new StringReader(@"Text: > - Some Text. - -Value: foo"); - var result = Deserializer.Deserialize(yamlReader); - - result.Should().NotBeNull(); - } - - [Fact] - public void SerializeCustomTags() - { - var expectedResult = Yaml.ReaderFrom("tags.yaml").ReadToEnd().NormalizeNewLines(); - SerializerBuilder - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) - .WithTagMapping(new TagName("tag:yaml.org,2002:point"), typeof(Point)); - - var point = new Point(10, 20); - var result = Serializer.Serialize(point); - - result.Should().Be(expectedResult); - } - - [Fact] - public void DeserializeExplicitType() - { - var text = Yaml.ReaderFrom("explicit-type.template").TemplatedOn(); - - var result = new DeserializerBuilder() - .WithTagMapping("!Simple", typeof(Simple)) - .Build() - .Deserialize(UsingReaderFor(text)); - - result.aaa.Should().Be("bbb"); - } - - [Fact] - public void DeserializeConvertible() - { - var text = Yaml.ReaderFrom("convertible.template").TemplatedOn(); - - var result = new DeserializerBuilder() - .WithTagMapping("!Convertible", typeof(Convertible)) - .Build() - .Deserialize(UsingReaderFor(text)); - - result.aaa.Should().Be("[hello, world]"); - } - - [Fact] - public void DeserializationFailsForUndefinedForwardReferences() - { - var text = Lines( - "Nothing: *forward", - "MyString: ForwardReference"); - - Action action = () => Deserializer.Deserialize(UsingReaderFor(text)); - - action.ShouldThrow(); - } - - [Fact] - public void RoundtripObject() - { - var obj = new Example(); - - var result = DoRoundtripFromObjectTo( - obj, - new SerializerBuilder() - .WithTagMapping("!Example", typeof(Example)) - .EnsureRoundtrip() - .Build(), - new DeserializerBuilder() - .WithTagMapping("!Example", typeof(Example)) - .Build() - ); - - result.ShouldBeEquivalentTo(obj); - } - - [Fact] - public void RoundtripObjectWithDefaults() - { - var obj = new Example(); - - var result = DoRoundtripFromObjectTo( - obj, - new SerializerBuilder() - .WithTagMapping("!Example", typeof(Example)) - .EnsureRoundtrip() - .Build(), - new DeserializerBuilder() - .WithTagMapping("!Example", typeof(Example)) - .Build() - ); - - result.ShouldBeEquivalentTo(obj); - } - - [Fact] - public void RoundtripAnonymousType() - { - var data = new { Key = 3 }; - - var result = DoRoundtripFromObjectTo>(data); - - result.Should().Equal(new Dictionary { - { "Key", "3" } - }); - } - - [Fact] - public void RoundtripWithYamlTypeConverter() - { - var obj = new MissingDefaultCtor("Yo"); - - SerializerBuilder - .EnsureRoundtrip() - .WithTypeConverter(new MissingDefaultCtorConverter()); - - DeserializerBuilder - .WithTypeConverter(new MissingDefaultCtorConverter()); - - var result = DoRoundtripFromObjectTo(obj, Serializer, Deserializer); - - result.Value.Should().Be("Yo"); - } - - [Fact] - public void RoundtripAlias() - { - var writer = new StringWriter(); - var input = new NameConvention { AliasTest = "Fourth" }; - - SerializerBuilder - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults); - - Serializer.Serialize(writer, input, input.GetType()); - var text = writer.ToString(); - - // Todo: use RegEx once FluentAssertions 2.2 is released - text.TrimEnd('\r', '\n').Should().Be("fourthTest: Fourth"); - - var output = Deserializer.Deserialize(UsingReaderFor(text)); - - output.AliasTest.Should().Be(input.AliasTest); - } - - [Fact] - public void RoundtripAliasOverride() - { - var writer = new StringWriter(); - var input = new NameConvention { AliasTest = "Fourth" }; - - var attribute = new YamlMemberAttribute - { - Alias = "fourthOverride" - }; - - var serializer = new SerializerBuilder() - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) - .WithAttributeOverride(nc => nc.AliasTest, attribute) - .Build(); - - serializer.Serialize(writer, input, input.GetType()); - var text = writer.ToString(); - - // Todo: use RegEx once FluentAssertions 2.2 is released - text.TrimEnd('\r', '\n').Should().Be("fourthOverride: Fourth"); - - DeserializerBuilder.WithAttributeOverride(n => n.AliasTest, attribute); - var output = Deserializer.Deserialize(UsingReaderFor(text)); - - output.AliasTest.Should().Be(input.AliasTest); - } - - [Fact] - // Todo: is the assert on the string necessary? - public void RoundtripDerivedClass() - { - var obj = new InheritanceExample - { - SomeScalar = "Hello", - RegularBase = new Derived { BaseProperty = "foo", DerivedProperty = "bar" } - }; - - var result = DoRoundtripFromObjectTo( - obj, - new SerializerBuilder() - .WithTagMapping("!InheritanceExample", typeof(InheritanceExample)) - .WithTagMapping("!Derived", typeof(Derived)) - .EnsureRoundtrip() - .Build(), - new DeserializerBuilder() - .WithTagMapping("!InheritanceExample", typeof(InheritanceExample)) - .WithTagMapping("!Derived", typeof(Derived)) - .Build() - ); - - result.SomeScalar.Should().Be("Hello"); - result.RegularBase.Should().BeOfType().And - .Subject.As().ShouldBeEquivalentTo(new { ChildProp = "bar" }, o => o.ExcludingMissingMembers()); - } - - [Fact] - public void RoundtripDerivedClassWithSerializeAs() - { - var obj = new InheritanceExample - { - SomeScalar = "Hello", - BaseWithSerializeAs = new Derived { BaseProperty = "foo", DerivedProperty = "bar" } - }; - - var result = DoRoundtripFromObjectTo( - obj, - new SerializerBuilder() - .WithTagMapping("!InheritanceExample", typeof(InheritanceExample)) - .EnsureRoundtrip() - .Build(), - new DeserializerBuilder() - .WithTagMapping("!InheritanceExample", typeof(InheritanceExample)) - .Build() - ); - - result.BaseWithSerializeAs.Should().BeOfType().And - .Subject.As().ShouldBeEquivalentTo(new { ParentProp = "foo" }, o => o.ExcludingMissingMembers()); - } - - [Fact] - public void RoundtripInterfaceProperties() - { - AssumingDeserializerWith(new LambdaObjectFactory(t => - { - if (t == typeof(InterfaceExample)) { return new InterfaceExample(); } - else if (t == typeof(IDerived)) { return new Derived(); } - return null; - })); - - var obj = new InterfaceExample - { - Derived = new Derived { BaseProperty = "foo", DerivedProperty = "bar" } - }; - - var result = DoRoundtripFromObjectTo(obj); - - result.Derived.Should().BeOfType().And - .Subject.As().ShouldBeEquivalentTo(new { BaseProperty = "foo", DerivedProperty = "bar" }, o => o.ExcludingMissingMembers()); - } - - [Fact] - public void DeserializeGuid() - { - var stream = Yaml.ReaderFrom("guid.yaml"); - var result = Deserializer.Deserialize(stream); - - result.Should().Be(new Guid("9462790d5c44468985425e2dd38ebd98")); - } - - [Fact] - public void DeserializationOfOrderedProperties() - { - var stream = Yaml.ReaderFrom("ordered-properties.yaml"); - - var orderExample = Deserializer.Deserialize(stream); - - orderExample.Order1.Should().Be("Order1 value"); - orderExample.Order2.Should().Be("Order2 value"); - } - - [Fact] - public void DeserializeEnumerable() - { - var obj = new[] { new Simple { aaa = "bbb" } }; - - var result = DoRoundtripFromObjectTo>(obj); - - result.Should().ContainSingle(item => "bbb".Equals(item.aaa)); - } - - [Fact] - public void DeserializeArray() - { - var stream = Yaml.ReaderFrom("list.yaml"); - - var result = Deserializer.Deserialize(stream); - - result.Should().Equal(new[] { "one", "two", "three" }); - } - - [Fact] - public void DeserializeList() - { - var stream = Yaml.ReaderFrom("list.yaml"); - - var result = Deserializer.Deserialize(stream); - - result.Should().BeAssignableTo().And - .Subject.As().Should().Equal(new[] { "one", "two", "three" }); - } - - [Fact] - public void DeserializeExplicitList() - { - var stream = Yaml.ReaderFrom("list-explicit.yaml"); - - var result = new DeserializerBuilder() - .WithTagMapping("!List", typeof(List)) - .Build() - .Deserialize(stream); - - result.Should().BeAssignableTo>().And - .Subject.As>().Should().Equal(3, 4, 5); - } - - [Fact] - public void RoundtripList() - { - var obj = new List { 2, 4, 6 }; - - var result = DoRoundtripOn>(obj, SerializerBuilder.EnsureRoundtrip().Build()); - - result.Should().Equal(obj); - } - - [Fact] - public void RoundtripArrayWithTypeConversion() - { - var obj = new object[] { 1, 2, "3" }; - - var result = DoRoundtripFromObjectTo(obj); - - result.Should().Equal(1, 2, 3); - } - - [Fact] - public void RoundtripArrayOfIdenticalObjects() - { - var z = new Simple { aaa = "bbb" }; - var obj = new[] { z, z, z }; - - var result = DoRoundtripOn(obj); - - result.Should().HaveCount(3).And.OnlyContain(x => z.aaa.Equals(x.aaa)); - result[0].Should().BeSameAs(result[1]).And.BeSameAs(result[2]); - } - - [Fact] - public void DeserializeDictionary() - { - var stream = Yaml.ReaderFrom("dictionary.yaml"); - - var result = Deserializer.Deserialize(stream); - - result.Should().BeAssignableTo>().And.Subject - .As>().Should().Equal(new Dictionary { - { "key1", "value1" }, - { "key2", "value2" } - }); - } - - [Fact] - public void DeserializeExplicitDictionary() - { - var stream = Yaml.ReaderFrom("dictionary-explicit.yaml"); - - var result = new DeserializerBuilder() - .WithTagMapping("!Dictionary", typeof(Dictionary)) - .Build() - .Deserialize(stream); - - result.Should().BeAssignableTo>().And.Subject - .As>().Should().Equal(new Dictionary { - { "key1", 1 }, - { "key2", 2 } - }); - } - - [Fact] - public void RoundtripDictionary() - { - var obj = new Dictionary { - { "key1", "value1" }, - { "key2", "value2" }, - { "key3", "value3" } - }; - - var result = DoRoundtripFromObjectTo>(obj); - - result.Should().Equal(obj); - } - - [Fact] - public void DeserializeListOfDictionaries() - { - var stream = Yaml.ReaderFrom("list-of-dictionaries.yaml"); - - var result = Deserializer.Deserialize>>(stream); - - result.ShouldBeEquivalentTo(new[] { - new Dictionary { - { "connection", "conn1" }, - { "path", "path1" } - }, - new Dictionary { - { "connection", "conn2" }, - { "path", "path2" } - }}, opt => opt.WithStrictOrderingFor(root => root)); - } - - [Fact] - public void DeserializeTwoDocuments() - { - var reader = ParserFor(Lines( - "---", - "aaa: 111", - "---", - "aaa: 222", - "...")); - - reader.Consume(); - var one = Deserializer.Deserialize(reader); - var two = Deserializer.Deserialize(reader); - - one.ShouldBeEquivalentTo(new { aaa = "111" }); - two.ShouldBeEquivalentTo(new { aaa = "222" }); - } - - [Fact] - public void DeserializeThreeDocuments() - { - var reader = ParserFor(Lines( - "---", - "aaa: 111", - "---", - "aaa: 222", - "---", - "aaa: 333", - "...")); - - reader.Consume(); - var one = Deserializer.Deserialize(reader); - var two = Deserializer.Deserialize(reader); - var three = Deserializer.Deserialize(reader); - - reader.Accept(out var _).Should().BeTrue("reader should have reached StreamEnd"); - one.ShouldBeEquivalentTo(new { aaa = "111" }); - two.ShouldBeEquivalentTo(new { aaa = "222" }); - three.ShouldBeEquivalentTo(new { aaa = "333" }); - } - - [Fact] - public void SerializeGuid() - { - var guid = new Guid("{9462790D-5C44-4689-8542-5E2DD38EBD98}"); - - var writer = new StringWriter(); - - Serializer.Serialize(writer, guid); - var serialized = writer.ToString(); - Regex.IsMatch(serialized, "^" + guid.ToString("D")).Should().BeTrue("serialized content should contain the guid, but instead contained: " + serialized); - } - - [Fact] - public void SerializationOfNullInListsAreAlwaysEmittedWithoutUsingEmitDefaults() - { - var writer = new StringWriter(); - var obj = new[] { "foo", null, "bar" }; - - Serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - Regex.Matches(serialized, "-").Count.Should().Be(3, "there should have been 3 elements"); - } - - [Fact] - public void SerializationOfNullInListsAreAlwaysEmittedWhenUsingEmitDefaults() - { - var writer = new StringWriter(); - var obj = new[] { "foo", null, "bar" }; - - SerializerBuilder.Build().Serialize(writer, obj); - var serialized = writer.ToString(); - - Regex.Matches(serialized, "-").Count.Should().Be(3, "there should have been 3 elements"); - } - - [Fact] - public void SerializationIncludesKeyWhenEmittingDefaults() - { - var writer = new StringWriter(); - var obj = new Example { MyString = null }; - - SerializerBuilder.Build().Serialize(writer, obj, typeof(Example)); - - writer.ToString().Should().Contain("MyString"); - } - - [Fact] - [Trait("Motive", "Bug fix")] - public void SerializationIncludesKeyFromAnonymousTypeWhenEmittingDefaults() - { - var writer = new StringWriter(); - var obj = new { MyString = (string)null }; - - SerializerBuilder.Build().Serialize(writer, obj, obj.GetType()); - - writer.ToString().Should().Contain("MyString"); - } - - [Fact] - public void SerializationDoesNotIncludeKeyWhenDisregardingDefaults() - { - var writer = new StringWriter(); - var obj = new Example { MyString = null }; - - SerializerBuilder - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults); - - Serializer.Serialize(writer, obj, typeof(Example)); - - writer.ToString().Should().NotContain("MyString"); - } - - [Fact] - public void SerializationOfDefaultsWorkInJson() - { - var writer = new StringWriter(); - var obj = new Example { MyString = null }; - - SerializerBuilder.JsonCompatible().Build().Serialize(writer, obj, typeof(Example)); - - writer.ToString().Should().Contain("MyString"); - } - - [Fact] - public void SerializationOfLongKeysWorksInJson() - { - var writer = new StringWriter(); - var obj = new Dictionary - { - { new string('x', 3000), "extremely long key" } - }; - - SerializerBuilder.JsonCompatible().Build().Serialize(writer, obj, typeof(Dictionary)); - - writer.ToString().Should().NotContain("?"); - } - - [Fact] - public void SerializationOfAnchorWorksInJson() - { - var deserializer = new DeserializerBuilder().Build(); - var yamlObject = deserializer.Deserialize(Yaml.ReaderForText(@" -x: &anchor1 - z: - v: 1 -y: - k: *anchor1")); - - var serializer = new SerializerBuilder() - .JsonCompatible() - .Build(); - - serializer.Serialize(yamlObject).Trim().Should() - .BeEquivalentTo(@"{""x"": {""z"": {""v"": ""1""}}, ""y"": {""k"": {""z"": {""v"": ""1""}}}}"); - } - - [Fact] - // Todo: this is actually roundtrip - public void DeserializationOfDefaultsWorkInJson() - { - var writer = new StringWriter(); - var obj = new Example { MyString = null }; - - SerializerBuilder.EnsureRoundtrip().JsonCompatible().Build().Serialize(writer, obj, typeof(Example)); - var result = Deserializer.Deserialize(UsingReaderFor(writer)); - - result.MyString.Should().BeNull(); - } - - [Fact] - public void NullsRoundTrip() - { - var writer = new StringWriter(); - var obj = new Example { MyString = null }; - - SerializerBuilder.EnsureRoundtrip().Build().Serialize(writer, obj, typeof(Example)); - var result = Deserializer.Deserialize(UsingReaderFor(writer)); - - result.MyString.Should().BeNull(); - } - - [Theory] - [InlineData(typeof(SByteEnum))] - [InlineData(typeof(ByteEnum))] - [InlineData(typeof(Int16Enum))] - [InlineData(typeof(UInt16Enum))] - [InlineData(typeof(Int32Enum))] - [InlineData(typeof(UInt32Enum))] - [InlineData(typeof(Int64Enum))] - [InlineData(typeof(UInt64Enum))] - public void DeserializationOfEnumWorksInJson(Type enumType) - { - var defaultEnumValue = 0; - var nonDefaultEnumValue = Enum.GetValues(enumType).GetValue(1); - - var jsonSerializer = SerializerBuilder.EnsureRoundtrip().JsonCompatible().Build(); - var jsonSerializedEnum = jsonSerializer.Serialize(nonDefaultEnumValue); - - nonDefaultEnumValue.Should().NotBe(defaultEnumValue); - jsonSerializedEnum.Should().Contain($"\"{nonDefaultEnumValue}\""); - } - - [Fact] - public void SerializationOfOrderedProperties() - { - var obj = new OrderExample(); - var writer = new StringWriter(); - - Serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should() - .Be("Order1: Order1 value\r\nOrder2: Order2 value\r\n".NormalizeNewLines(), "the properties should be in the right order"); - } - - [Fact] - public void SerializationRespectsYamlIgnoreAttribute() - { - - var writer = new StringWriter(); - var obj = new IgnoreExample(); - - Serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should().NotContain("IgnoreMe"); - } - - [Fact] - public void SerializationRespectsYamlIgnoreAttributeOfDerivedClasses() - { - - var writer = new StringWriter(); - var obj = new IgnoreExampleDerived(); - - Serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should().NotContain("IgnoreMe"); - } - - [Fact] - public void SerializationRespectsYamlIgnoreOverride() - { - - var writer = new StringWriter(); - var obj = new Simple(); - - var ignore = new YamlIgnoreAttribute(); - var serializer = new SerializerBuilder() - .WithAttributeOverride(s => s.aaa, ignore) - .Build(); - - serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should().NotContain("aaa"); - } - - [Fact] - public void SerializationRespectsScalarStyle() - { - var writer = new StringWriter(); - var obj = new ScalarStyleExample(); - - Serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should() - .Be("LiteralString: |-\r\n Test\r\nDoubleQuotedString: \"Test\"\r\n".NormalizeNewLines(), "the properties should be specifically styled"); - } - - [Fact] - public void SerializationRespectsScalarStyleOverride() - { - var writer = new StringWriter(); - var obj = new ScalarStyleExample(); - - var serializer = new SerializerBuilder() - .WithAttributeOverride(e => e.LiteralString, new YamlMemberAttribute { ScalarStyle = ScalarStyle.DoubleQuoted }) - .WithAttributeOverride(e => e.DoubleQuotedString, new YamlMemberAttribute { ScalarStyle = ScalarStyle.Literal }) - .Build(); - - serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should() - .Be("LiteralString: \"Test\"\r\nDoubleQuotedString: |-\r\n Test\r\n".NormalizeNewLines(), "the properties should be specifically styled"); - } - - [Fact] - public void SerializationDerivedAttributeOverride() - { - var writer = new StringWriter(); - var obj = new Derived { DerivedProperty = "Derived", BaseProperty = "Base" }; - - var ignore = new YamlIgnoreAttribute(); - var serializer = new SerializerBuilder() - .WithAttributeOverride(d => d.DerivedProperty, ignore) - .Build(); - - serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should() - .Be("BaseProperty: Base\r\n".NormalizeNewLines(), "the derived property should be specifically ignored"); - } - - [Fact] - public void SerializationBaseAttributeOverride() - { - var writer = new StringWriter(); - var obj = new Derived { DerivedProperty = "Derived", BaseProperty = "Base" }; - - var ignore = new YamlIgnoreAttribute(); - var serializer = new SerializerBuilder() - .WithAttributeOverride(b => b.BaseProperty, ignore) - .Build(); - - serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should() - .Be("DerivedProperty: Derived\r\n".NormalizeNewLines(), "the base property should be specifically ignored"); - } - - [Fact] - public void SerializationSkipsPropertyWhenUsingDefaultValueAttribute() - { - var writer = new StringWriter(); - var obj = new DefaultsExample { Value = DefaultsExample.DefaultValue }; - - SerializerBuilder - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults); - - Serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should().NotContain("Value"); - } - - [Fact] - public void SerializationEmitsPropertyWhenUsingEmitDefaultsAndDefaultValueAttribute() - { - var writer = new StringWriter(); - var obj = new DefaultsExample { Value = DefaultsExample.DefaultValue }; - - SerializerBuilder.Build().Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should().Contain("Value"); - } - - [Fact] - public void SerializationEmitsPropertyWhenValueDifferFromDefaultValueAttribute() - { - var writer = new StringWriter(); - var obj = new DefaultsExample { Value = "non-default" }; - - Serializer.Serialize(writer, obj); - var serialized = writer.ToString(); - - serialized.Should().Contain("Value"); - } - - [Fact] - public void SerializingAGenericDictionaryShouldNotThrowTargetException() - { - var obj = new CustomGenericDictionary { - { "hello", "world" } - }; - - Action action = () => Serializer.Serialize(new StringWriter(), obj); - - action.ShouldNotThrow(); - } - - [Fact] - public void SerializationUtilizeNamingConventions() - { - var convention = A.Fake(); - A.CallTo(() => convention.Apply(A._)).ReturnsLazily((string x) => x); - var obj = new NameConvention { FirstTest = "1", SecondTest = "2" }; - - var serializer = new SerializerBuilder() - .WithNamingConvention(convention) - .Build(); - - serializer.Serialize(new StringWriter(), obj); - - A.CallTo(() => convention.Apply("FirstTest")).MustHaveHappened(); - A.CallTo(() => convention.Apply("SecondTest")).MustHaveHappened(); - } - - [Fact] - public void DeserializationUtilizeNamingConventions() - { - var convention = A.Fake(); - A.CallTo(() => convention.Apply(A._)).ReturnsLazily((string x) => x); - var text = Lines( - "FirstTest: 1", - "SecondTest: 2"); - - var deserializer = new DeserializerBuilder() - .WithNamingConvention(convention) - .Build(); - - deserializer.Deserialize(UsingReaderFor(text)); - - A.CallTo(() => convention.Apply("FirstTest")).MustHaveHappened(); - A.CallTo(() => convention.Apply("SecondTest")).MustHaveHappened(); - } - - [Fact] - public void TypeConverterIsUsedOnListItems() - { - var text = Lines( - "- !{type}", - " Left: hello", - " Right: world") - .TemplatedOn(); - - var list = new DeserializerBuilder() - .WithTagMapping("!Convertible", typeof(Convertible)) - .Build() - .Deserialize>(UsingReaderFor(text)); - - list - .Should().NotBeNull() - .And.ContainSingle(c => c.Equals("[hello, world]")); - } - - [Fact] - public void BackreferencesAreMergedWithMappings() - { - var stream = Yaml.ReaderFrom("backreference.yaml"); - - var parser = new MergingParser(new Parser(stream)); - var result = Deserializer.Deserialize>>(parser); - - var alias = result["alias"]; - alias.Should() - .Contain("key1", "value1", "key1 should be inherited from the backreferenced mapping") - .And.Contain("key2", "Overriding key2", "key2 should be overriden by the actual mapping") - .And.Contain("key3", "value3", "key3 is defined in the actual mapping"); - } - - [Fact] - public void MergingDoesNotProduceDuplicateAnchors() - { - var parser = new MergingParser(Yaml.ParserForText(@" - anchor: &default - key1: &myValue value1 - key2: value2 - alias: - <<: *default - key2: Overriding key2 - key3: value3 - useMyValue: - key: *myValue - ")); - var result = Deserializer.Deserialize>>(parser); - - var alias = result["alias"]; - alias.Should() - .Contain("key1", "value1", "key1 should be inherited from the backreferenced mapping") - .And.Contain("key2", "Overriding key2", "key2 should be overriden by the actual mapping") - .And.Contain("key3", "value3", "key3 is defined in the actual mapping"); - - result["useMyValue"].Should() - .Contain("key", "value1", "key should be copied"); - } - - [Fact] - public void ExampleFromSpecificationIsHandledCorrectly() - { - var parser = new MergingParser(Yaml.ParserForText(@" - obj: - - &CENTER { x: 1, y: 2 } - - &LEFT { x: 0, y: 2 } - - &BIG { r: 10 } - - &SMALL { r: 1 } - - # All the following maps are equal: - results: - - # Explicit keys - x: 1 - y: 2 - r: 10 - label: center/big - - - # Merge one map - << : *CENTER - r: 10 - label: center/big - - - # Merge multiple maps - << : [ *CENTER, *BIG ] - label: center/big - - - # Override - << : [ *BIG, *LEFT, *SMALL ] - x: 1 - label: center/big - ")); - - var result = Deserializer.Deserialize>>>(parser); - - var index = 0; - foreach (var mapping in result["results"]) - { - mapping.Should() - .Contain("x", "1", "'x' should be '1' in result #{0}", index) - .And.Contain("y", "2", "'y' should be '2' in result #{0}", index) - .And.Contain("r", "10", "'r' should be '10' in result #{0}", index) - .And.Contain("label", "center/big", "'label' should be 'center/big' in result #{0}", index); - - ++index; - } - } - - [Fact] - public void MergeNestedReferenceCorrectly() - { - var parser = new MergingParser(Yaml.ParserForText(@" - base1: &level1 - key: X - level: 1 - base2: &level2 - <<: *level1 - key: Y - level: 2 - derived1: - <<: *level1 - key: D1 - derived2: - <<: *level2 - key: D2 - derived3: - <<: [ *level1, *level2 ] - key: D3 - ")); - - var result = Deserializer.Deserialize>>(parser); - - result["derived1"].Should() - .Contain("key", "D1", "key should be overriden by the actual mapping") - .And.Contain("level", "1", "level should be inherited from the backreferenced mapping"); - - result["derived2"].Should() - .Contain("key", "D2", "key should be overriden by the actual mapping") - .And.Contain("level", "2", "level should be inherited from the backreferenced mapping"); - - result["derived3"].Should() - .Contain("key", "D3", "key should be overriden by the actual mapping") - .And.Contain("level", "1", "level should be inherited from the backreferenced mapping"); - } - - [Fact] - public void IgnoreExtraPropertiesIfWanted() - { - var text = Lines("aaa: hello", "bbb: world"); - DeserializerBuilder.IgnoreUnmatchedProperties(); - var actual = Deserializer.Deserialize(UsingReaderFor(text)); - actual.aaa.Should().Be("hello"); - } - - [Fact] - public void DontIgnoreExtraPropertiesIfWanted() - { - var text = Lines("aaa: hello", "bbb: world"); - var actual = Record.Exception(() => Deserializer.Deserialize(UsingReaderFor(text))); - Assert.IsType(actual); - ((YamlException)actual).Start.Column.Should().Be(1); - ((YamlException)actual).Start.Line.Should().Be(2); - ((YamlException)actual).Start.Index.Should().Be(12); - ((YamlException)actual).End.Column.Should().Be(4); - ((YamlException)actual).End.Line.Should().Be(2); - ((YamlException)actual).End.Index.Should().Be(15); - ((YamlException)actual).Message.Should().Be("Property 'bbb' not found on type 'YamlDotNet.Test.Serialization.Simple'."); - } - - [Fact] - public void IgnoreExtraPropertiesIfWantedBefore() - { - var text = Lines("bbb: [200,100]", "aaa: hello"); - DeserializerBuilder.IgnoreUnmatchedProperties(); - var actual = Deserializer.Deserialize(UsingReaderFor(text)); - actual.aaa.Should().Be("hello"); - } - - [Fact] - public void IgnoreExtraPropertiesIfWantedNamingScheme() - { - var text = Lines( - "scratch: 'scratcher'", - "deleteScratch: false", - "notScratch: 9443", - "notScratch: 192.168.1.30", - "mappedScratch:", - "- '/work/'" - ); - - DeserializerBuilder - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .IgnoreUnmatchedProperties(); - - var actual = Deserializer.Deserialize(UsingReaderFor(text)); - actual.Scratch.Should().Be("scratcher"); - actual.DeleteScratch.Should().Be(false); - actual.MappedScratch.Should().ContainInOrder(new[] { "/work/" }); - } - - [Fact] - public void InvalidTypeConversionsProduceProperExceptions() - { - var text = Lines("- 1", "- two", "- 3"); - - var sut = new Deserializer(); - var exception = Assert.Throws(() => sut.Deserialize>(UsingReaderFor(text))); - - Assert.Equal(2, exception.Start.Line); - Assert.Equal(3, exception.Start.Column); - } - - [Theory] - [InlineData("blah")] - [InlineData("hello=world")] - [InlineData("+190:20:30")] - [InlineData("x:y")] - public void ValueAllowedAfterDocumentStartToken(string text) - { - var value = Lines("--- " + text); - - var sut = new Deserializer(); - var actual = sut.Deserialize(UsingReaderFor(value)); - - Assert.Equal(text, actual); - } - - [Fact] - public void MappingDisallowedAfterDocumentStartToken() - { - var value = Lines("--- x: y"); - - var sut = new Deserializer(); - var exception = Assert.Throws(() => sut.Deserialize(UsingReaderFor(value))); - - Assert.Equal(1, exception.Start.Line); - Assert.Equal(6, exception.Start.Column); - } - - [Fact] - public void SerializeDynamicPropertyAndApplyNamingConvention() - { - dynamic obj = new ExpandoObject(); - obj.property_one = new ExpandoObject(); - ((IDictionary)obj.property_one).Add("new_key_here", "new_value"); - - var mockNamingConvention = A.Fake(); - A.CallTo(() => mockNamingConvention.Apply(A.Ignored)).Returns("xxx"); - - var serializer = new SerializerBuilder() - .WithNamingConvention(mockNamingConvention) - .Build(); - - var writer = new StringWriter(); - serializer.Serialize(writer, obj); - - writer.ToString().Should().Contain("xxx: new_value"); - } - - [Fact] - public void SerializeGenericDictionaryPropertyAndDoNotApplyNamingConvention() - { - var obj = new Dictionary - { - ["property_one"] = new GenericTestDictionary() - }; - - ((IDictionary)obj["property_one"]).Add("new_key_here", "new_value"); - - var mockNamingConvention = A.Fake(); - A.CallTo(() => mockNamingConvention.Apply(A.Ignored)).Returns("xxx"); - - var serializer = new SerializerBuilder() - .WithNamingConvention(mockNamingConvention) - .Build(); - - var writer = new StringWriter(); - serializer.Serialize(writer, obj); - - writer.ToString().Should().Contain("new_key_here: new_value"); - } - - [Theory, MemberData(nameof(SpecialFloats))] - public void SpecialFloatsAreHandledCorrectly(FloatTestCase testCase) - { - var buffer = new StringWriter(); - Serializer.Serialize(buffer, testCase.Value); - - var firstLine = buffer.ToString().Split('\r', '\n')[0]; - Assert.Equal(testCase.ExpectedTextRepresentation, firstLine); - - var deserializer = new Deserializer(); - var deserializedValue = deserializer.Deserialize(new StringReader(buffer.ToString()), testCase.Value.GetType()); - - Assert.Equal(testCase.Value, deserializedValue); - } - - [Fact] - public void EmptyStringsAreQuoted() - { - var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); - var o = new { test = string.Empty }; - var result = serializer.Serialize(o); - var expected = $"test: \"\"{Environment.NewLine}"; - Assert.Equal(expected, result); - } - - public class FloatTestCase - { - private readonly string description; - public object Value { get; private set; } - public string ExpectedTextRepresentation { get; private set; } - - public FloatTestCase(string description, object value, string expectedTextRepresentation) - { - this.description = description; - Value = value; - ExpectedTextRepresentation = expectedTextRepresentation; - } - - public override string ToString() - { - return description; - } - } - - public static IEnumerable SpecialFloats - { - get - { - return - new[] - { - new FloatTestCase("double.NaN", double.NaN, ".nan"), - new FloatTestCase("double.PositiveInfinity", double.PositiveInfinity, ".inf"), - new FloatTestCase("double.NegativeInfinity", double.NegativeInfinity, "-.inf"), - new FloatTestCase("double.Epsilon", double.Epsilon, double.Epsilon.ToString("G", CultureInfo.InvariantCulture)), - new FloatTestCase("double.26.67", 26.67D, "26.67"), - - new FloatTestCase("float.NaN", float.NaN, ".nan"), - new FloatTestCase("float.PositiveInfinity", float.PositiveInfinity, ".inf"), - new FloatTestCase("float.NegativeInfinity", float.NegativeInfinity, "-.inf"), - new FloatTestCase("float.Epsilon", float.Epsilon, float.Epsilon.ToString("G", CultureInfo.InvariantCulture)), - new FloatTestCase("float.26.67", 26.67F, "26.67"), - -#if NETCOREAPP3_1_OR_GREATER - new FloatTestCase("double.MinValue", double.MinValue, double.MinValue.ToString("G", CultureInfo.InvariantCulture)), - new FloatTestCase("double.MaxValue", double.MaxValue, double.MaxValue.ToString("G", CultureInfo.InvariantCulture)), - new FloatTestCase("float.MinValue", float.MinValue, float.MinValue.ToString("G", CultureInfo.InvariantCulture)), - new FloatTestCase("float.MaxValue", float.MaxValue, float.MaxValue.ToString("G", CultureInfo.InvariantCulture)), -#endif - } - .Select(tc => new object[] { tc }); - } - } - - [Fact] - public void NegativeIntegersCanBeDeserialized() - { - var deserializer = new Deserializer(); - - var value = deserializer.Deserialize(Yaml.ReaderForText(@" - '-123' - ")); - Assert.Equal(-123, value); - } - - [Fact] - public void GenericDictionaryThatDoesNotImplementIDictionaryCanBeDeserialized() - { - var sut = new Deserializer(); - var deserialized = sut.Deserialize>(Yaml.ReaderForText(@" - a: 1 - b: 2 - ")); - - Assert.Equal("1", deserialized["a"]); - Assert.Equal("2", deserialized["b"]); - } - - [Fact] - public void GenericListThatDoesNotImplementIListCanBeDeserialized() - { - var sut = new Deserializer(); - var deserialized = sut.Deserialize>(Yaml.ReaderForText(@" - - a - - b - ")); - - Assert.Contains("a", deserialized); - Assert.Contains("b", deserialized); - } - - [Fact] - public void GuidsShouldBeQuotedWhenSerializedAsJson() - { - var sut = new SerializerBuilder() - .JsonCompatible() - .Build(); - - var yamlAsJson = new StringWriter(); - sut.Serialize(yamlAsJson, new - { - id = Guid.Empty - }); - - Assert.Contains("\"00000000-0000-0000-0000-000000000000\"", yamlAsJson.ToString()); - } - - public class Foo - { - public bool IsRequired { get; set; } - } - - [Fact] - public void AttributeOverridesAndNamingConventionDoNotConflict() - { - var namingConvention = CamelCaseNamingConvention.Instance; - - var yamlMember = new YamlMemberAttribute - { - Alias = "Required" - }; - - var serializer = new SerializerBuilder() - .WithNamingConvention(namingConvention) - .WithAttributeOverride(f => f.IsRequired, yamlMember) - .Build(); - - var yaml = serializer.Serialize(new Foo { IsRequired = true }); - Assert.Contains("required: true", yaml); - - var deserializer = new DeserializerBuilder() - .WithNamingConvention(namingConvention) - .WithAttributeOverride(f => f.IsRequired, yamlMember) - .Build(); - - var deserializedFoo = deserializer.Deserialize(yaml); - Assert.True(deserializedFoo.IsRequired); - } - - [Fact] - public void YamlConvertiblesAreAbleToEmitAndParseComments() - { - var serializer = new Serializer(); - var yaml = serializer.Serialize(new CommentWrapper { Comment = "A comment", Value = "The value" }); - - var deserializer = new Deserializer(); - var parser = new Parser(new Scanner(new StringReader(yaml), skipComments: false)); - var parsed = deserializer.Deserialize>(parser); - - Assert.Equal("A comment", parsed.Comment); - Assert.Equal("The value", parsed.Value); - } - - public class CommentWrapper : IYamlConvertible - { - public string Comment { get; set; } - public T Value { get; set; } - - public void Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) - { - if (parser.TryConsume(out var comment)) - { - Comment = comment.Value; - } - - Value = (T)nestedObjectDeserializer(typeof(T)); - } - - public void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) - { - if (!string.IsNullOrEmpty(Comment)) - { - emitter.Emit(new Comment(Comment, false)); - } - - nestedObjectSerializer(Value, typeof(T)); - } - } - - [Theory] - [InlineData(uint.MinValue)] - [InlineData(uint.MaxValue)] - [InlineData(0x8000000000000000UL)] - public void DeserializationOfUInt64Succeeds(ulong value) - { - var yaml = new Serializer().Serialize(value); - Assert.Contains(value.ToString(), yaml); - - var parsed = new Deserializer().Deserialize(yaml); - Assert.Equal(value, parsed); - } - - [Theory] - [InlineData(int.MinValue)] - [InlineData(int.MaxValue)] - [InlineData(0L)] - public void DeserializationOfInt64Succeeds(long value) - { - var yaml = new Serializer().Serialize(value); - Assert.Contains(value.ToString(), yaml); - - var parsed = new Deserializer().Deserialize(yaml); - Assert.Equal(value, parsed); - } - - public class AnchorsOverwritingTestCase - { - public List a { get; set; } - public List b { get; set; } - public List c { get; set; } - public List d { get; set; } - } - - [Fact] - public void DeserializationOfStreamWithDuplicateAnchorsSucceeds() - { - var yaml = Yaml.ParserForResource("anchors-overwriting.yaml"); - var serializer = new DeserializerBuilder() - .IgnoreUnmatchedProperties() - .Build(); - var deserialized = serializer.Deserialize(yaml); - Assert.NotNull(deserialized); - } - - private sealed class AnchorPrecedence - { - internal sealed class AnchorPrecedenceNested - { - public string b1 { get; set; } - public Dictionary b2 { get; set; } - } - - public string a { get; set; } - public AnchorPrecedenceNested b { get; set; } - public string c { get; set; } - } - - [Fact] - public void DeserializationWithDuplicateAnchorsSucceeds() - { - var sut = new Deserializer(); - var deserialized = sut.Deserialize(@" -a: &anchor1 test0 -b: - b1: &anchor1 test1 - b2: - b21: &anchor1 test2 -c: *anchor1"); - - Assert.Equal("test0", deserialized.a); - Assert.Equal("test1", deserialized.b.b1); - Assert.Contains("b21", deserialized.b.b2.Keys); - Assert.Equal("test2", deserialized.b.b2["b21"]); - Assert.Equal("test2", deserialized.c); - } - - [Fact] - public void SerializeExceptionWithStackTrace() - { - var ex = GetExceptionWithStackTrace(); - var serializer = new SerializerBuilder() - .WithTypeConverter(new MethodInfoConverter()) - .Build(); - var yaml = serializer.Serialize(ex); - Assert.Contains("GetExceptionWithStackTrace", yaml); - } - - private class MethodInfoConverter : IYamlTypeConverter - { - public bool Accepts(Type type) - { - return typeof(MethodInfo).IsAssignableFrom(type); - } - - public object ReadYaml(IParser parser, Type type) - { - throw new NotImplementedException(); - } - - public void WriteYaml(IEmitter emitter, object value, Type type) - { - var method = (MethodInfo)value; - emitter.Emit(new Scalar(string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name))); - } - } - - static Exception GetExceptionWithStackTrace() - { - try - { - throw new ArgumentNullException("foo"); - } - catch (Exception ex) - { - return ex; - } - } - - [Fact] - public void RegisteringATypeConverterPreventsTheTypeFromBeingVisited() - { - var serializer = new SerializerBuilder() - .WithTypeConverter(new NonSerializableTypeConverter()) - .Build(); - - var yaml = serializer.Serialize(new NonSerializableContainer - { - Value = new NonSerializable { Text = "hello" }, - }); - - var deserializer = new DeserializerBuilder() - .WithTypeConverter(new NonSerializableTypeConverter()) - .Build(); - - var result = deserializer.Deserialize(yaml); - - Assert.Equal("hello", result.Value.Text); - } - - [Fact] - public void NamingConventionIsNotAppliedBySerializerWhenApplyNamingConventionsIsFalse() - { - var sut = new SerializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); - - var yaml = sut.Serialize(new NamingConventionDisabled { NoConvention = "value" }); - - Assert.Contains("NoConvention", yaml); - } - - [Fact] - public void NamingConventionIsNotAppliedByDeserializerWhenApplyNamingConventionsIsFalse() - { - var sut = new DeserializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); - - var yaml = "NoConvention: value"; - - var parsed = sut.Deserialize(yaml); - - Assert.Equal("value", parsed.NoConvention); - } - - [Fact] - public void TypesAreSerializable() - { - var sut = new SerializerBuilder() - .Build(); - - var yaml = sut.Serialize(typeof(string)); - - Assert.Contains(typeof(string).AssemblyQualifiedName, yaml); - } - - [Fact] - public void TypesAreDeserializable() - { - var sut = new DeserializerBuilder() - .Build(); - - var type = sut.Deserialize(typeof(string).AssemblyQualifiedName); - - Assert.Equal(typeof(string), type); - } - - [Fact] - public void TypesAreConvertedWhenNeededFromScalars() - { - var sut = new DeserializerBuilder() - .WithTagMapping("!dbl", typeof(DoublyConverted)) - .Build(); - - var result = sut.Deserialize("!dbl hello"); - - Assert.Equal(5, result); - } - - [Fact] - public void TypesAreConvertedWhenNeededInsideLists() - { - var sut = new DeserializerBuilder() - .WithTagMapping("!dbl", typeof(DoublyConverted)) - .Build(); - - var result = sut.Deserialize>("- !dbl hello"); - - Assert.Equal(5, result[0]); - } - - [Fact] - public void TypesAreConvertedWhenNeededInsideDictionary() - { - var sut = new DeserializerBuilder() - .WithTagMapping("!dbl", typeof(DoublyConverted)) - .Build(); - - var result = sut.Deserialize>("!dbl hello: !dbl you"); - - Assert.True(result.ContainsKey(5)); - Assert.Equal(3, result[5]); - } - - [Fact] - public void InfiniteRecursionIsDetected() - { - var sut = new SerializerBuilder() - .DisableAliases() - .Build(); - - var recursionRoot = new - { - Nested = new[] - { - new Dictionary() - } - }; - - recursionRoot.Nested[0].Add("loop", recursionRoot); - - var exception = Assert.Throws(() => sut.Serialize(recursionRoot)); - } - - [Fact] - public void TuplesAreSerializable() - { - var sut = new SerializerBuilder() - .Build(); - - var yaml = sut.Serialize(new[] - { - Tuple.Create(1, "one"), - Tuple.Create(2, "two"), - }); - - var expected = Yaml.Text(@" - - Item1: 1 - Item2: one - - Item1: 2 - Item2: two - "); - - Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); - } - - [Fact] - public void ValueTuplesAreSerializableWithoutMetadata() - { - var sut = new SerializerBuilder() - .Build(); - - var yaml = sut.Serialize(new[] - { - (num: 1, txt: "one"), - (num: 2, txt: "two"), - }); - - var expected = Yaml.Text(@" - - Item1: 1 - Item2: one - - Item1: 2 - Item2: two - "); - - Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); - } - - [Fact] - public void AnchorNameWithTrailingColonReferencedInKeyCanBeDeserialized() - { - var sut = new Deserializer(); - var deserialized = sut.Deserialize>(Yaml.ReaderForText(@" - a: &::::scaryanchor:::: anchor "" value "" - *::::scaryanchor::::: 2 - myvalue: *::::scaryanchor:::: - ")); - - Assert.Equal(@"anchor "" value """, deserialized["a"]); - Assert.Equal("2", deserialized[@"anchor "" value """]); - Assert.Equal(@"anchor "" value """, deserialized["myvalue"]); - } - - [Fact] - public void AliasBeforeAnchorCannotBeDeserialized() - { - var sut = new Deserializer(); - Action action = () => sut.Deserialize>(@" -a: *anchor1 -b: &anchor1 test0 -c: *anchor1"); - - action.ShouldThrow(); - } - - [Fact] - public void AnchorWithAllowedCharactersCanBeDeserialized() - { - var sut = new Deserializer(); - var deserialized = sut.Deserialize>(Yaml.ReaderForText(@" - a: &@nchor<>""@-_123$>>>😁🎉🐻🍔end some value - myvalue: my *@nchor<>""@-_123$>>>😁🎉🐻🍔end test - interpolated value: *@nchor<>""@-_123$>>>😁🎉🐻🍔end - ")); - - Assert.Equal("some value", deserialized["a"]); - Assert.Equal(@"my *@nchor<>""@-_123$>>>😁🎉🐻🍔end test", deserialized["myvalue"]); - Assert.Equal("some value", deserialized["interpolated value"]); - } - - [Fact] - public void SerializationNonPublicPropertiesAreIgnored() - { - var sut = new SerializerBuilder().Build(); - var yaml = sut.Serialize(new NonPublicPropertiesExample()); - Assert.Equal("Public: public", yaml.TrimNewLines()); - } - - [Fact] - public void SerializationNonPublicPropertiesAreIncluded() - { - var sut = new SerializerBuilder().IncludeNonPublicProperties().Build(); - var yaml = sut.Serialize(new NonPublicPropertiesExample()); - - var expected = Yaml.Text(@" - Public: public - Internal: internal - Protected: protected - Private: private - "); - - Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); - } - - [Fact] - public void DeserializationNonPublicPropertiesAreIgnored() - { - var sut = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); - var deserialized = sut.Deserialize(Yaml.ReaderForText(@" - Public: public2 - Internal: internal2 - Protected: protected2 - Private: private2 - ")); - - Assert.Equal("public2,internal,protected,private", deserialized.ToString()); - } - - [Fact] - public void DeserializationNonPublicPropertiesAreIncluded() - { - var sut = new DeserializerBuilder().IncludeNonPublicProperties().Build(); - var deserialized = sut.Deserialize(Yaml.ReaderForText(@" - Public: public2 - Internal: internal2 - Protected: protected2 - Private: private2 - ")); - - Assert.Equal("public2,internal2,protected2,private2", deserialized.ToString()); - } - - [Fact] - public void SerializationNonPublicFieldsAreIgnored() - { - var sut = new SerializerBuilder().Build(); - var yaml = sut.Serialize(new NonPublicFieldsExample()); - Assert.Equal("Public: public", yaml.TrimNewLines()); - } - - [Fact] - public void DeserializationNonPublicFieldsAreIgnored() - { - var sut = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); - var deserialized = sut.Deserialize(Yaml.ReaderForText(@" - Public: public2 - Internal: internal2 - Protected: protected2 - Private: private2 - ")); - - Assert.Equal("public2,internal,protected,private", deserialized.ToString()); - } - - [Fact] - public void ShouldNotIndentSequences() - { - var sut = new SerializerBuilder() - .Build(); - - var yaml = sut.Serialize(new - { - first = "first", - items = new[] - { - "item1", - "item2" - }, - nested = new[] - { - new - { - name = "name1", - more = new[] - { - "nested1", - "nested2" - } - } - } - }); - - var expected = Yaml.Text(@" - first: first - items: - - item1 - - item2 - nested: - - name: name1 - more: - - nested1 - - nested2 - "); - - Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); - } - - [Fact] - public void ShouldIndentSequences() - { - var sut = new SerializerBuilder() - .WithIndentedSequences() - .Build(); - - var yaml = sut.Serialize(new - { - first = "first", - items = new[] - { - "item1", - "item2" - }, - nested = new[] - { - new - { - name = "name1", - more = new[] - { - "nested1", - "nested2" - } - } - } - }); - - var expected = Yaml.Text(@" - first: first - items: - - item1 - - item2 - nested: - - name: name1 - more: - - nested1 - - nested2 - "); - - Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); - } - - [Fact] - public void ExampleFromSpecificationIsHandledCorrectlyWithLateDefine() - { - var parser = new MergingParser(Yaml.ParserForText(@" - # All the following maps are equal: - results: - - # Explicit keys - x: 1 - y: 2 - r: 10 - label: center/big - - - # Merge one map - << : *CENTER - r: 10 - label: center/big - - - # Merge multiple maps - << : [ *CENTER, *BIG ] - label: center/big - - - # Override - << : [ *BIG, *LEFT, *SMALL ] - x: 1 - label: center/big - - obj: - - &CENTER { x: 1, y: 2 } - - &LEFT { x: 0, y: 2 } - - &SMALL { r: 1 } - - &BIG { r: 10 } - ")); - - var result = Deserializer.Deserialize>>>(parser); - - int index = 0; - foreach (var mapping in result["results"]) - { - mapping.Should() - .Contain("x", "1", "'x' should be '1' in result #{0}", index) - .And.Contain("y", "2", "'y' should be '2' in result #{0}", index) - .And.Contain("r", "10", "'r' should be '10' in result #{0}", index) - .And.Contain("label", "center/big", "'label' should be 'center/big' in result #{0}", index); - - ++index; - } - } - - public class CycleTestEntity - { - public CycleTestEntity Cycle { get; set; } - } - - [Fact] - public void SerializeCycleWithAlias() - { - var sut = new SerializerBuilder() - .WithTagMapping("!CycleTag", typeof(CycleTestEntity)) - .Build(); - - var entity = new CycleTestEntity(); - entity.Cycle = entity; - var yaml = sut.Serialize(entity); - var expected = Yaml.Text(@"&o0 !CycleTag -Cycle: *o0"); - - Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); - } - - [Fact] - public void DeserializeCycleWithAlias() - { - var sut = new DeserializerBuilder() - .WithTagMapping("!CycleTag", typeof(CycleTestEntity)) - .Build(); - - var yaml = Yaml.Text(@"&o0 !CycleTag -Cycle: *o0"); - var obj = sut.Deserialize(yaml); - - Assert.Same(obj, obj.Cycle); - } - - [Fact] - public void DeserializeCycleWithoutAlias() - { - var sut = new DeserializerBuilder() - .Build(); - - var yaml = Yaml.Text(@"&o0 -Cycle: *o0"); - var obj = sut.Deserialize(yaml); - - Assert.Same(obj, obj.Cycle); - } - - public static IEnumerable Depths => Enumerable.Range(1, 10).Select(i => new[] { (object)i }); - - [Theory] - [MemberData(nameof(Depths))] - public void DeserializeCycleWithAnchorsWithDepth(int? depth) - { - var sut = new DeserializerBuilder() - .WithTagMapping("!CycleTag", typeof(CycleTestEntity)) - .Build(); - - StringBuilder builder = new StringBuilder(@"&o0 !CycleTag"); - builder.AppendLine(); - string indentation; - for (int i = 0; i < depth - 1; ++i) - { - indentation = string.Concat(Enumerable.Repeat(" ", i)); - builder.AppendLine($"{indentation}Cycle: !CycleTag"); - } - indentation = string.Concat(Enumerable.Repeat(" ", depth.Value - 1)); - builder.AppendLine($"{indentation}Cycle: *o0"); - var yaml = Yaml.Text(builder.ToString()); - var obj = sut.Deserialize(yaml); - CycleTestEntity iterator = obj; - for (int i = 0; i < depth; ++i) - { - iterator = iterator.Cycle; - } - Assert.Same(obj, iterator); - } - - [Fact] - public void RoundtripWindowsNewlines() - { - var text = $"Line1{Environment.NewLine}Line2{Environment.NewLine}Line3{Environment.NewLine}{Environment.NewLine}Line4"; - - var sut = new SerializerBuilder().Build(); - var dut = new DeserializerBuilder().Build(); - - using var writer = new StringWriter { NewLine = Environment.NewLine }; - sut.Serialize(writer, new StringContainer { Text = text }); - var serialized = writer.ToString(); - - using var reader = new StringReader(serialized); - var roundtrippedText = dut.Deserialize(reader).Text.NormalizeNewLines(); - Assert.Equal(text, roundtrippedText); - } - - [Theory] - [InlineData("NULL")] - [InlineData("Null")] - [InlineData("null")] - [InlineData("~")] - [InlineData("true")] - [InlineData("false")] - [InlineData("True")] - [InlineData("False")] - [InlineData("TRUE")] - [InlineData("FALSE")] - [InlineData("0o77")] - [InlineData("0x7A")] - [InlineData("+1e10")] - [InlineData("1E10")] - [InlineData("+.inf")] - [InlineData("-.inf")] - [InlineData(".inf")] - [InlineData(".nan")] - [InlineData(".NaN")] - [InlineData(".NAN")] - public void StringsThatMatchKeywordsAreQuoted(string input) - { - var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); - var o = new { text = input }; - var yaml = serializer.Serialize(o); - Assert.Equal($"text: \"{input}\"{Environment.NewLine}", yaml); - } - - [Fact] - public void KeysOnConcreteClassDontGetQuoted_TypeStringGetsQuoted() - { - var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); - var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); - var yaml = @" -True: null -False: hello -Null: true -"; - var obj = deserializer.Deserialize>(yaml); - var result = serializer.Serialize(obj); - obj.True.Should().BeNull(); - obj.False.Should().Be("hello"); - obj.Null.Should().Be("true"); - result.Should().Be($"True: {Environment.NewLine}False: hello{Environment.NewLine}Null: \"true\"{Environment.NewLine}"); - } - - [Fact] - public void KeysOnConcreteClassDontGetQuoted_TypeBoolDoesNotGetQuoted() - { - var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); - var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); - var yaml = @" -True: null -False: hello -Null: true -"; - var obj = deserializer.Deserialize>(yaml); - var result = serializer.Serialize(obj); - obj.True.Should().BeNull(); - obj.False.Should().Be("hello"); - obj.Null.Should().BeTrue(); - result.Should().Be($"True: {Environment.NewLine}False: hello{Environment.NewLine}Null: true{Environment.NewLine}"); - } - - public class ReservedWordsTestClass - { - public string True { get; set; } - public string False { get; set; } - public TNullType Null { get; set; } - } - - [TypeConverter(typeof(DoublyConvertedTypeConverter))] - public class DoublyConverted - { - public string Value { get; set; } - } - - public class DoublyConvertedTypeConverter : TypeConverter - { - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - return destinationType == typeof(int); - } - - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) - { - return ((DoublyConverted)value).Value.Length; - } - - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - return sourceType == typeof(string); - } - - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) - { - return new DoublyConverted { Value = (string)value }; - } - } - - public class NamingConventionDisabled - { - [YamlMember(ApplyNamingConventions = false)] - public string NoConvention { get; set; } - } - - public class NonSerializableContainer - { - public NonSerializable Value { get; set; } - } - - public class NonSerializable - { - public string WillThrow { get { throw new Exception(); } } - - public string Text { get; set; } - } - - public class StringContainer - { - public string Text { get; set; } - } - - public class NonSerializableTypeConverter : IYamlTypeConverter - { - public bool Accepts(Type type) - { - return typeof(NonSerializable).IsAssignableFrom(type); - } - - public object ReadYaml(IParser parser, Type type) - { - var scalar = parser.Consume(); - return new NonSerializable { Text = scalar.Value }; - } - - public void WriteYaml(IEmitter emitter, object value, Type type) - { - emitter.Emit(new Scalar(((NonSerializable)value).Text)); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Dynamic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using FakeItEasy; +using FluentAssertions; +using Xunit; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; +using YamlDotNet.Serialization.ObjectFactories; + +namespace YamlDotNet.Test.Serialization +{ + public class SerializationTests : SerializationTestHelper + { + #region Test Cases + + private static readonly string[] TrueStrings = { "true", "y", "yes", "on" }; + private static readonly string[] FalseStrings = { "false", "n", "no", "off" }; + + public static IEnumerable DeserializeScalarBoolean_TestCases + { + get + { + foreach (var trueString in TrueStrings) + { + yield return new object[] { trueString, true }; + yield return new object[] { trueString.ToUpper(), true }; + } + + foreach (var falseString in FalseStrings) + { + yield return new object[] { falseString, false }; + yield return new object[] { falseString.ToUpper(), false }; + } + } + } + + #endregion + + [Fact] + public void DeserializeEmptyDocument() + { + var emptyText = string.Empty; + + var array = Deserializer.Deserialize(UsingReaderFor(emptyText)); + + array.Should().BeNull(); + } + + [Fact] + public void DeserializeScalar() + { + var stream = Yaml.ReaderFrom("02-scalar-in-imp-doc.yaml"); + + var result = Deserializer.Deserialize(stream); + + result.Should().Be("a scalar"); + } + + [Theory] + [MemberData(nameof(DeserializeScalarBoolean_TestCases))] + public void DeserializeScalarBoolean(string value, bool expected) + { + var result = Deserializer.Deserialize(UsingReaderFor(value)); + + result.Should().Be(expected); + } + + [Fact] + public void DeserializeScalarBooleanThrowsWhenInvalid() + { + Action action = () => Deserializer.Deserialize(UsingReaderFor("not-a-boolean")); + + action.ShouldThrow().WithInnerException(); + } + + [Fact] + public void DeserializeScalarZero() + { + var result = Deserializer.Deserialize(UsingReaderFor("0")); + + result.Should().Be(0); + } + + [Fact] + public void DeserializeScalarDecimal() + { + var result = Deserializer.Deserialize(UsingReaderFor("+1_234_567")); + + result.Should().Be(1234567); + } + + [Fact] + public void DeserializeScalarBinaryNumber() + { + var result = Deserializer.Deserialize(UsingReaderFor("-0b1_0010_1001_0010")); + + result.Should().Be(-4754); + } + + [Fact] + public void DeserializeScalarOctalNumber() + { + var result = Deserializer.Deserialize(UsingReaderFor("+071_352")); + + result.Should().Be(29418); + } + + [Fact] + public void DeserializeNullableScalarOctalNumber() + { + var result = Deserializer.Deserialize(UsingReaderFor("+071_352")); + + result.Should().Be(29418); + } + + [Fact] + public void DeserializeScalarHexNumber() + { + var result = Deserializer.Deserialize(UsingReaderFor("-0x_0F_B9")); + + result.Should().Be(-0xFB9); + } + + [Fact] + public void DeserializeScalarLongBase60Number() + { + var result = Deserializer.Deserialize(UsingReaderFor("99_:_58:47:3:6_2:10")); + + result.Should().Be(77744246530L); + } + + [Theory] + [InlineData(EnumExample.One)] + [InlineData(EnumExample.One | EnumExample.Two)] + public void RoundtripEnums(EnumExample value) + { + var result = DoRoundtripFromObjectTo(value); + + result.Should().Be(value); + } + + [Theory] + [InlineData(EnumExample.One)] + [InlineData(EnumExample.One | EnumExample.Two)] + [InlineData(null)] + public void RoundtripNullableEnums(EnumExample? value) + { + var result = DoRoundtripFromObjectTo(value); + + result.Should().Be(value); + } + + [Fact] + public void RoundtripNullableStructWithValue() + { + var value = new StructExample { Value = 2 }; + + var result = DoRoundtripFromObjectTo(value); + + result.Should().Be(value); + } + + [Fact] + public void RoundtripNullableStructWithoutValue() + { + var result = DoRoundtripFromObjectTo(null); + + result.Should().Be(null); + } + + [Fact] + public void SerializeCircularReference() + { + var obj = new CircularReference(); + obj.Child1 = new CircularReference + { + Child1 = obj, + Child2 = obj + }; + + Action action = () => SerializerBuilder.EnsureRoundtrip().Build().Serialize(new StringWriter(), obj, typeof(CircularReference)); + + action.ShouldNotThrow(); + } + + [Fact] + public void DeserializeIncompleteDirective() + { + Action action = () => Deserializer.Deserialize(UsingReaderFor("%Y")); + + action.ShouldThrow() + .WithMessage("While scanning a directive, found unexpected end of stream."); + } + + [Fact] + public void DeserializeSkippedReservedDirective() + { + Action action = () => Deserializer.Deserialize(UsingReaderFor("%Y ")); + + action.ShouldNotThrow(); + } + + [Fact] + public void DeserializeCustomTags() + { + var stream = Yaml.ReaderFrom("tags.yaml"); + + DeserializerBuilder.WithTagMapping("tag:yaml.org,2002:point", typeof(Point)); + var result = Deserializer.Deserialize(stream); + + result.Should().BeOfType().And + .Subject.As() + .ShouldBeEquivalentTo(new { X = 10, Y = 20 }, o => o.ExcludingMissingMembers()); + } + + [Fact] + public void DeserializeWithGapsBetweenKeys() + { + var yamlReader = new StringReader(@"Text: > + Some Text. + +Value: foo"); + var result = Deserializer.Deserialize(yamlReader); + + result.Should().NotBeNull(); + } + + [Fact] + public void SerializeCustomTags() + { + var expectedResult = Yaml.ReaderFrom("tags.yaml").ReadToEnd().NormalizeNewLines(); + SerializerBuilder + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) + .WithTagMapping(new TagName("tag:yaml.org,2002:point"), typeof(Point)); + + var point = new Point(10, 20); + var result = Serializer.Serialize(point); + + result.Should().Be(expectedResult); + } + + [Fact] + public void DeserializeExplicitType() + { + var text = Yaml.ReaderFrom("explicit-type.template").TemplatedOn(); + + var result = new DeserializerBuilder() + .WithTagMapping("!Simple", typeof(Simple)) + .Build() + .Deserialize(UsingReaderFor(text)); + + result.aaa.Should().Be("bbb"); + } + + [Fact] + public void DeserializeConvertible() + { + var text = Yaml.ReaderFrom("convertible.template").TemplatedOn(); + + var result = new DeserializerBuilder() + .WithTagMapping("!Convertible", typeof(Convertible)) + .Build() + .Deserialize(UsingReaderFor(text)); + + result.aaa.Should().Be("[hello, world]"); + } + + [Fact] + public void DeserializationFailsForUndefinedForwardReferences() + { + var text = Lines( + "Nothing: *forward", + "MyString: ForwardReference"); + + Action action = () => Deserializer.Deserialize(UsingReaderFor(text)); + + action.ShouldThrow(); + } + + [Fact] + public void RoundtripObject() + { + var obj = new Example(); + + var result = DoRoundtripFromObjectTo( + obj, + new SerializerBuilder() + .WithTagMapping("!Example", typeof(Example)) + .EnsureRoundtrip() + .Build(), + new DeserializerBuilder() + .WithTagMapping("!Example", typeof(Example)) + .Build() + ); + + result.ShouldBeEquivalentTo(obj); + } + + [Fact] + public void RoundtripObjectWithDefaults() + { + var obj = new Example(); + + var result = DoRoundtripFromObjectTo( + obj, + new SerializerBuilder() + .WithTagMapping("!Example", typeof(Example)) + .EnsureRoundtrip() + .Build(), + new DeserializerBuilder() + .WithTagMapping("!Example", typeof(Example)) + .Build() + ); + + result.ShouldBeEquivalentTo(obj); + } + + [Fact] + public void RoundtripAnonymousType() + { + var data = new { Key = 3 }; + + var result = DoRoundtripFromObjectTo>(data); + + result.Should().Equal(new Dictionary { + { "Key", "3" } + }); + } + + [Fact] + public void RoundtripWithYamlTypeConverter() + { + var obj = new MissingDefaultCtor("Yo"); + + SerializerBuilder + .EnsureRoundtrip() + .WithTypeConverter(new MissingDefaultCtorConverter()); + + DeserializerBuilder + .WithTypeConverter(new MissingDefaultCtorConverter()); + + var result = DoRoundtripFromObjectTo(obj, Serializer, Deserializer); + + result.Value.Should().Be("Yo"); + } + + [Fact] + public void RoundtripAlias() + { + var writer = new StringWriter(); + var input = new NameConvention { AliasTest = "Fourth" }; + + SerializerBuilder + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults); + + Serializer.Serialize(writer, input, input.GetType()); + var text = writer.ToString(); + + // Todo: use RegEx once FluentAssertions 2.2 is released + text.TrimEnd('\r', '\n').Should().Be("fourthTest: Fourth"); + + var output = Deserializer.Deserialize(UsingReaderFor(text)); + + output.AliasTest.Should().Be(input.AliasTest); + } + + [Fact] + public void RoundtripAliasOverride() + { + var writer = new StringWriter(); + var input = new NameConvention { AliasTest = "Fourth" }; + + var attribute = new YamlMemberAttribute + { + Alias = "fourthOverride" + }; + + var serializer = new SerializerBuilder() + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) + .WithAttributeOverride(nc => nc.AliasTest, attribute) + .Build(); + + serializer.Serialize(writer, input, input.GetType()); + var text = writer.ToString(); + + // Todo: use RegEx once FluentAssertions 2.2 is released + text.TrimEnd('\r', '\n').Should().Be("fourthOverride: Fourth"); + + DeserializerBuilder.WithAttributeOverride(n => n.AliasTest, attribute); + var output = Deserializer.Deserialize(UsingReaderFor(text)); + + output.AliasTest.Should().Be(input.AliasTest); + } + + [Fact] + // Todo: is the assert on the string necessary? + public void RoundtripDerivedClass() + { + var obj = new InheritanceExample + { + SomeScalar = "Hello", + RegularBase = new Derived { BaseProperty = "foo", DerivedProperty = "bar" } + }; + + var result = DoRoundtripFromObjectTo( + obj, + new SerializerBuilder() + .WithTagMapping("!InheritanceExample", typeof(InheritanceExample)) + .WithTagMapping("!Derived", typeof(Derived)) + .EnsureRoundtrip() + .Build(), + new DeserializerBuilder() + .WithTagMapping("!InheritanceExample", typeof(InheritanceExample)) + .WithTagMapping("!Derived", typeof(Derived)) + .Build() + ); + + result.SomeScalar.Should().Be("Hello"); + result.RegularBase.Should().BeOfType().And + .Subject.As().ShouldBeEquivalentTo(new { ChildProp = "bar" }, o => o.ExcludingMissingMembers()); + } + + [Fact] + public void RoundtripDerivedClassWithSerializeAs() + { + var obj = new InheritanceExample + { + SomeScalar = "Hello", + BaseWithSerializeAs = new Derived { BaseProperty = "foo", DerivedProperty = "bar" } + }; + + var result = DoRoundtripFromObjectTo( + obj, + new SerializerBuilder() + .WithTagMapping("!InheritanceExample", typeof(InheritanceExample)) + .EnsureRoundtrip() + .Build(), + new DeserializerBuilder() + .WithTagMapping("!InheritanceExample", typeof(InheritanceExample)) + .Build() + ); + + result.BaseWithSerializeAs.Should().BeOfType().And + .Subject.As().ShouldBeEquivalentTo(new { ParentProp = "foo" }, o => o.ExcludingMissingMembers()); + } + + [Fact] + public void RoundtripInterfaceProperties() + { + AssumingDeserializerWith(new LambdaObjectFactory(t => + { + if (t == typeof(InterfaceExample)) { return new InterfaceExample(); } + else if (t == typeof(IDerived)) { return new Derived(); } + return null; + })); + + var obj = new InterfaceExample + { + Derived = new Derived { BaseProperty = "foo", DerivedProperty = "bar" } + }; + + var result = DoRoundtripFromObjectTo(obj); + + result.Derived.Should().BeOfType().And + .Subject.As().ShouldBeEquivalentTo(new { BaseProperty = "foo", DerivedProperty = "bar" }, o => o.ExcludingMissingMembers()); + } + + [Fact] + public void DeserializeGuid() + { + var stream = Yaml.ReaderFrom("guid.yaml"); + var result = Deserializer.Deserialize(stream); + + result.Should().Be(new Guid("9462790d5c44468985425e2dd38ebd98")); + } + + [Fact] + public void DeserializationOfOrderedProperties() + { + var stream = Yaml.ReaderFrom("ordered-properties.yaml"); + + var orderExample = Deserializer.Deserialize(stream); + + orderExample.Order1.Should().Be("Order1 value"); + orderExample.Order2.Should().Be("Order2 value"); + } + + [Fact] + public void DeserializeEnumerable() + { + var obj = new[] { new Simple { aaa = "bbb" } }; + + var result = DoRoundtripFromObjectTo>(obj); + + result.Should().ContainSingle(item => "bbb".Equals(item.aaa)); + } + + [Fact] + public void DeserializeArray() + { + var stream = Yaml.ReaderFrom("list.yaml"); + + var result = Deserializer.Deserialize(stream); + + result.Should().Equal(new[] { "one", "two", "three" }); + } + + [Fact] + public void DeserializeList() + { + var stream = Yaml.ReaderFrom("list.yaml"); + + var result = Deserializer.Deserialize(stream); + + result.Should().BeAssignableTo().And + .Subject.As().Should().Equal(new[] { "one", "two", "three" }); + } + + [Fact] + public void DeserializeExplicitList() + { + var stream = Yaml.ReaderFrom("list-explicit.yaml"); + + var result = new DeserializerBuilder() + .WithTagMapping("!List", typeof(List)) + .Build() + .Deserialize(stream); + + result.Should().BeAssignableTo>().And + .Subject.As>().Should().Equal(3, 4, 5); + } + + [Fact] + public void RoundtripList() + { + var obj = new List { 2, 4, 6 }; + + var result = DoRoundtripOn>(obj, SerializerBuilder.EnsureRoundtrip().Build()); + + result.Should().Equal(obj); + } + + [Fact] + public void RoundtripArrayWithTypeConversion() + { + var obj = new object[] { 1, 2, "3" }; + + var result = DoRoundtripFromObjectTo(obj); + + result.Should().Equal(1, 2, 3); + } + + [Fact] + public void RoundtripArrayOfIdenticalObjects() + { + var z = new Simple { aaa = "bbb" }; + var obj = new[] { z, z, z }; + + var result = DoRoundtripOn(obj); + + result.Should().HaveCount(3).And.OnlyContain(x => z.aaa.Equals(x.aaa)); + result[0].Should().BeSameAs(result[1]).And.BeSameAs(result[2]); + } + + [Fact] + public void DeserializeDictionary() + { + var stream = Yaml.ReaderFrom("dictionary.yaml"); + + var result = Deserializer.Deserialize(stream); + + result.Should().BeAssignableTo>().And.Subject + .As>().Should().Equal(new Dictionary { + { "key1", "value1" }, + { "key2", "value2" } + }); + } + + [Fact] + public void DeserializeExplicitDictionary() + { + var stream = Yaml.ReaderFrom("dictionary-explicit.yaml"); + + var result = new DeserializerBuilder() + .WithTagMapping("!Dictionary", typeof(Dictionary)) + .Build() + .Deserialize(stream); + + result.Should().BeAssignableTo>().And.Subject + .As>().Should().Equal(new Dictionary { + { "key1", 1 }, + { "key2", 2 } + }); + } + + [Fact] + public void RoundtripDictionary() + { + var obj = new Dictionary { + { "key1", "value1" }, + { "key2", "value2" }, + { "key3", "value3" } + }; + + var result = DoRoundtripFromObjectTo>(obj); + + result.Should().Equal(obj); + } + + [Fact] + public void DeserializeListOfDictionaries() + { + var stream = Yaml.ReaderFrom("list-of-dictionaries.yaml"); + + var result = Deserializer.Deserialize>>(stream); + + result.ShouldBeEquivalentTo(new[] { + new Dictionary { + { "connection", "conn1" }, + { "path", "path1" } + }, + new Dictionary { + { "connection", "conn2" }, + { "path", "path2" } + }}, opt => opt.WithStrictOrderingFor(root => root)); + } + + [Fact] + public void DeserializeTwoDocuments() + { + var reader = ParserFor(Lines( + "---", + "aaa: 111", + "---", + "aaa: 222", + "...")); + + reader.Consume(); + var one = Deserializer.Deserialize(reader); + var two = Deserializer.Deserialize(reader); + + one.ShouldBeEquivalentTo(new { aaa = "111" }); + two.ShouldBeEquivalentTo(new { aaa = "222" }); + } + + [Fact] + public void DeserializeThreeDocuments() + { + var reader = ParserFor(Lines( + "---", + "aaa: 111", + "---", + "aaa: 222", + "---", + "aaa: 333", + "...")); + + reader.Consume(); + var one = Deserializer.Deserialize(reader); + var two = Deserializer.Deserialize(reader); + var three = Deserializer.Deserialize(reader); + + reader.Accept(out var _).Should().BeTrue("reader should have reached StreamEnd"); + one.ShouldBeEquivalentTo(new { aaa = "111" }); + two.ShouldBeEquivalentTo(new { aaa = "222" }); + three.ShouldBeEquivalentTo(new { aaa = "333" }); + } + + [Fact] + public void SerializeGuid() + { + var guid = new Guid("{9462790D-5C44-4689-8542-5E2DD38EBD98}"); + + var writer = new StringWriter(); + + Serializer.Serialize(writer, guid); + var serialized = writer.ToString(); + Regex.IsMatch(serialized, "^" + guid.ToString("D")).Should().BeTrue("serialized content should contain the guid, but instead contained: " + serialized); + } + + [Fact] + public void SerializationOfNullInListsAreAlwaysEmittedWithoutUsingEmitDefaults() + { + var writer = new StringWriter(); + var obj = new[] { "foo", null, "bar" }; + + Serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + Regex.Matches(serialized, "-").Count.Should().Be(3, "there should have been 3 elements"); + } + + [Fact] + public void SerializationOfNullInListsAreAlwaysEmittedWhenUsingEmitDefaults() + { + var writer = new StringWriter(); + var obj = new[] { "foo", null, "bar" }; + + SerializerBuilder.Build().Serialize(writer, obj); + var serialized = writer.ToString(); + + Regex.Matches(serialized, "-").Count.Should().Be(3, "there should have been 3 elements"); + } + + [Fact] + public void SerializationIncludesKeyWhenEmittingDefaults() + { + var writer = new StringWriter(); + var obj = new Example { MyString = null }; + + SerializerBuilder.Build().Serialize(writer, obj, typeof(Example)); + + writer.ToString().Should().Contain("MyString"); + } + + [Fact] + [Trait("Motive", "Bug fix")] + public void SerializationIncludesKeyFromAnonymousTypeWhenEmittingDefaults() + { + var writer = new StringWriter(); + var obj = new { MyString = (string)null }; + + SerializerBuilder.Build().Serialize(writer, obj, obj.GetType()); + + writer.ToString().Should().Contain("MyString"); + } + + [Fact] + public void SerializationDoesNotIncludeKeyWhenDisregardingDefaults() + { + var writer = new StringWriter(); + var obj = new Example { MyString = null }; + + SerializerBuilder + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults); + + Serializer.Serialize(writer, obj, typeof(Example)); + + writer.ToString().Should().NotContain("MyString"); + } + + [Fact] + public void SerializationOfDefaultsWorkInJson() + { + var writer = new StringWriter(); + var obj = new Example { MyString = null }; + + SerializerBuilder.JsonCompatible().Build().Serialize(writer, obj, typeof(Example)); + + writer.ToString().Should().Contain("MyString"); + } + + [Fact] + public void SerializationOfLongKeysWorksInJson() + { + var writer = new StringWriter(); + var obj = new Dictionary + { + { new string('x', 3000), "extremely long key" } + }; + + SerializerBuilder.JsonCompatible().Build().Serialize(writer, obj, typeof(Dictionary)); + + writer.ToString().Should().NotContain("?"); + } + + [Fact] + public void SerializationOfAnchorWorksInJson() + { + var deserializer = new DeserializerBuilder().Build(); + var yamlObject = deserializer.Deserialize(Yaml.ReaderForText(@" +x: &anchor1 + z: + v: 1 +y: + k: *anchor1")); + + var serializer = new SerializerBuilder() + .JsonCompatible() + .Build(); + + serializer.Serialize(yamlObject).Trim().Should() + .BeEquivalentTo(@"{""x"": {""z"": {""v"": ""1""}}, ""y"": {""k"": {""z"": {""v"": ""1""}}}}"); + } + + [Fact] + // Todo: this is actually roundtrip + public void DeserializationOfDefaultsWorkInJson() + { + var writer = new StringWriter(); + var obj = new Example { MyString = null }; + + SerializerBuilder.EnsureRoundtrip().JsonCompatible().Build().Serialize(writer, obj, typeof(Example)); + var result = Deserializer.Deserialize(UsingReaderFor(writer)); + + result.MyString.Should().BeNull(); + } + + [Fact] + public void NullsRoundTrip() + { + var writer = new StringWriter(); + var obj = new Example { MyString = null }; + + SerializerBuilder.EnsureRoundtrip().Build().Serialize(writer, obj, typeof(Example)); + var result = Deserializer.Deserialize(UsingReaderFor(writer)); + + result.MyString.Should().BeNull(); + } + + [Theory] + [InlineData(typeof(SByteEnum))] + [InlineData(typeof(ByteEnum))] + [InlineData(typeof(Int16Enum))] + [InlineData(typeof(UInt16Enum))] + [InlineData(typeof(Int32Enum))] + [InlineData(typeof(UInt32Enum))] + [InlineData(typeof(Int64Enum))] + [InlineData(typeof(UInt64Enum))] + public void DeserializationOfEnumWorksInJson(Type enumType) + { + var defaultEnumValue = 0; + var nonDefaultEnumValue = Enum.GetValues(enumType).GetValue(1); + + var jsonSerializer = SerializerBuilder.EnsureRoundtrip().JsonCompatible().Build(); + var jsonSerializedEnum = jsonSerializer.Serialize(nonDefaultEnumValue); + + nonDefaultEnumValue.Should().NotBe(defaultEnumValue); + jsonSerializedEnum.Should().Contain($"\"{nonDefaultEnumValue}\""); + } + + [Fact] + public void SerializationOfOrderedProperties() + { + var obj = new OrderExample(); + var writer = new StringWriter(); + + Serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should() + .Be("Order1: Order1 value\r\nOrder2: Order2 value\r\n".NormalizeNewLines(), "the properties should be in the right order"); + } + + [Fact] + public void SerializationRespectsYamlIgnoreAttribute() + { + + var writer = new StringWriter(); + var obj = new IgnoreExample(); + + Serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should().NotContain("IgnoreMe"); + } + + [Fact] + public void SerializationRespectsYamlIgnoreAttributeOfDerivedClasses() + { + + var writer = new StringWriter(); + var obj = new IgnoreExampleDerived(); + + Serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should().NotContain("IgnoreMe"); + } + + [Fact] + public void SerializationRespectsYamlIgnoreOverride() + { + + var writer = new StringWriter(); + var obj = new Simple(); + + var ignore = new YamlIgnoreAttribute(); + var serializer = new SerializerBuilder() + .WithAttributeOverride(s => s.aaa, ignore) + .Build(); + + serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should().NotContain("aaa"); + } + + [Fact] + public void SerializationRespectsScalarStyle() + { + var writer = new StringWriter(); + var obj = new ScalarStyleExample(); + + Serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should() + .Be("LiteralString: |-\r\n Test\r\nDoubleQuotedString: \"Test\"\r\n".NormalizeNewLines(), "the properties should be specifically styled"); + } + + [Fact] + public void SerializationRespectsScalarStyleOverride() + { + var writer = new StringWriter(); + var obj = new ScalarStyleExample(); + + var serializer = new SerializerBuilder() + .WithAttributeOverride(e => e.LiteralString, new YamlMemberAttribute { ScalarStyle = ScalarStyle.DoubleQuoted }) + .WithAttributeOverride(e => e.DoubleQuotedString, new YamlMemberAttribute { ScalarStyle = ScalarStyle.Literal }) + .Build(); + + serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should() + .Be("LiteralString: \"Test\"\r\nDoubleQuotedString: |-\r\n Test\r\n".NormalizeNewLines(), "the properties should be specifically styled"); + } + + [Fact] + public void SerializationDerivedAttributeOverride() + { + var writer = new StringWriter(); + var obj = new Derived { DerivedProperty = "Derived", BaseProperty = "Base" }; + + var ignore = new YamlIgnoreAttribute(); + var serializer = new SerializerBuilder() + .WithAttributeOverride(d => d.DerivedProperty, ignore) + .Build(); + + serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should() + .Be("BaseProperty: Base\r\n".NormalizeNewLines(), "the derived property should be specifically ignored"); + } + + [Fact] + public void SerializationBaseAttributeOverride() + { + var writer = new StringWriter(); + var obj = new Derived { DerivedProperty = "Derived", BaseProperty = "Base" }; + + var ignore = new YamlIgnoreAttribute(); + var serializer = new SerializerBuilder() + .WithAttributeOverride(b => b.BaseProperty, ignore) + .Build(); + + serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should() + .Be("DerivedProperty: Derived\r\n".NormalizeNewLines(), "the base property should be specifically ignored"); + } + + [Fact] + public void SerializationSkipsPropertyWhenUsingDefaultValueAttribute() + { + var writer = new StringWriter(); + var obj = new DefaultsExample { Value = DefaultsExample.DefaultValue }; + + SerializerBuilder + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults); + + Serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should().NotContain("Value"); + } + + [Fact] + public void SerializationEmitsPropertyWhenUsingEmitDefaultsAndDefaultValueAttribute() + { + var writer = new StringWriter(); + var obj = new DefaultsExample { Value = DefaultsExample.DefaultValue }; + + SerializerBuilder.Build().Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should().Contain("Value"); + } + + [Fact] + public void SerializationEmitsPropertyWhenValueDifferFromDefaultValueAttribute() + { + var writer = new StringWriter(); + var obj = new DefaultsExample { Value = "non-default" }; + + Serializer.Serialize(writer, obj); + var serialized = writer.ToString(); + + serialized.Should().Contain("Value"); + } + + [Fact] + public void SerializingAGenericDictionaryShouldNotThrowTargetException() + { + var obj = new CustomGenericDictionary { + { "hello", "world" } + }; + + Action action = () => Serializer.Serialize(new StringWriter(), obj); + + action.ShouldNotThrow(); + } + + [Fact] + public void SerializationUtilizeNamingConventions() + { + var convention = A.Fake(); + A.CallTo(() => convention.Apply(A._)).ReturnsLazily((string x) => x); + var obj = new NameConvention { FirstTest = "1", SecondTest = "2" }; + + var serializer = new SerializerBuilder() + .WithNamingConvention(convention) + .Build(); + + serializer.Serialize(new StringWriter(), obj); + + A.CallTo(() => convention.Apply("FirstTest")).MustHaveHappened(); + A.CallTo(() => convention.Apply("SecondTest")).MustHaveHappened(); + } + + [Fact] + public void DeserializationUtilizeNamingConventions() + { + var convention = A.Fake(); + A.CallTo(() => convention.Apply(A._)).ReturnsLazily((string x) => x); + var text = Lines( + "FirstTest: 1", + "SecondTest: 2"); + + var deserializer = new DeserializerBuilder() + .WithNamingConvention(convention) + .Build(); + + deserializer.Deserialize(UsingReaderFor(text)); + + A.CallTo(() => convention.Apply("FirstTest")).MustHaveHappened(); + A.CallTo(() => convention.Apply("SecondTest")).MustHaveHappened(); + } + + [Fact] + public void TypeConverterIsUsedOnListItems() + { + var text = Lines( + "- !{type}", + " Left: hello", + " Right: world") + .TemplatedOn(); + + var list = new DeserializerBuilder() + .WithTagMapping("!Convertible", typeof(Convertible)) + .Build() + .Deserialize>(UsingReaderFor(text)); + + list + .Should().NotBeNull() + .And.ContainSingle(c => c.Equals("[hello, world]")); + } + + [Fact] + public void BackreferencesAreMergedWithMappings() + { + var stream = Yaml.ReaderFrom("backreference.yaml"); + + var parser = new MergingParser(new Parser(stream)); + var result = Deserializer.Deserialize>>(parser); + + var alias = result["alias"]; + alias.Should() + .Contain("key1", "value1", "key1 should be inherited from the backreferenced mapping") + .And.Contain("key2", "Overriding key2", "key2 should be overriden by the actual mapping") + .And.Contain("key3", "value3", "key3 is defined in the actual mapping"); + } + + [Fact] + public void MergingDoesNotProduceDuplicateAnchors() + { + var parser = new MergingParser(Yaml.ParserForText(@" + anchor: &default + key1: &myValue value1 + key2: value2 + alias: + <<: *default + key2: Overriding key2 + key3: value3 + useMyValue: + key: *myValue + ")); + var result = Deserializer.Deserialize>>(parser); + + var alias = result["alias"]; + alias.Should() + .Contain("key1", "value1", "key1 should be inherited from the backreferenced mapping") + .And.Contain("key2", "Overriding key2", "key2 should be overriden by the actual mapping") + .And.Contain("key3", "value3", "key3 is defined in the actual mapping"); + + result["useMyValue"].Should() + .Contain("key", "value1", "key should be copied"); + } + + [Fact] + public void ExampleFromSpecificationIsHandledCorrectly() + { + var parser = new MergingParser(Yaml.ParserForText(@" + obj: + - &CENTER { x: 1, y: 2 } + - &LEFT { x: 0, y: 2 } + - &BIG { r: 10 } + - &SMALL { r: 1 } + + # All the following maps are equal: + results: + - # Explicit keys + x: 1 + y: 2 + r: 10 + label: center/big + + - # Merge one map + << : *CENTER + r: 10 + label: center/big + + - # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + + - # Override + << : [ *BIG, *LEFT, *SMALL ] + x: 1 + label: center/big + ")); + + var result = Deserializer.Deserialize>>>(parser); + + var index = 0; + foreach (var mapping in result["results"]) + { + mapping.Should() + .Contain("x", "1", "'x' should be '1' in result #{0}", index) + .And.Contain("y", "2", "'y' should be '2' in result #{0}", index) + .And.Contain("r", "10", "'r' should be '10' in result #{0}", index) + .And.Contain("label", "center/big", "'label' should be 'center/big' in result #{0}", index); + + ++index; + } + } + + [Fact] + public void MergeNestedReferenceCorrectly() + { + var parser = new MergingParser(Yaml.ParserForText(@" + base1: &level1 + key: X + level: 1 + base2: &level2 + <<: *level1 + key: Y + level: 2 + derived1: + <<: *level1 + key: D1 + derived2: + <<: *level2 + key: D2 + derived3: + <<: [ *level1, *level2 ] + key: D3 + ")); + + var result = Deserializer.Deserialize>>(parser); + + result["derived1"].Should() + .Contain("key", "D1", "key should be overriden by the actual mapping") + .And.Contain("level", "1", "level should be inherited from the backreferenced mapping"); + + result["derived2"].Should() + .Contain("key", "D2", "key should be overriden by the actual mapping") + .And.Contain("level", "2", "level should be inherited from the backreferenced mapping"); + + result["derived3"].Should() + .Contain("key", "D3", "key should be overriden by the actual mapping") + .And.Contain("level", "1", "level should be inherited from the backreferenced mapping"); + } + + [Fact] + public void IgnoreExtraPropertiesIfWanted() + { + var text = Lines("aaa: hello", "bbb: world"); + DeserializerBuilder.IgnoreUnmatchedProperties(); + var actual = Deserializer.Deserialize(UsingReaderFor(text)); + actual.aaa.Should().Be("hello"); + } + + [Fact] + public void DontIgnoreExtraPropertiesIfWanted() + { + var text = Lines("aaa: hello", "bbb: world"); + var actual = Record.Exception(() => Deserializer.Deserialize(UsingReaderFor(text))); + Assert.IsType(actual); + ((YamlException)actual).Start.Column.Should().Be(1); + ((YamlException)actual).Start.Line.Should().Be(2); + ((YamlException)actual).Start.Index.Should().Be(12); + ((YamlException)actual).End.Column.Should().Be(4); + ((YamlException)actual).End.Line.Should().Be(2); + ((YamlException)actual).End.Index.Should().Be(15); + ((YamlException)actual).Message.Should().Be("Property 'bbb' not found on type 'YamlDotNet.Test.Serialization.Simple'."); + } + + [Fact] + public void IgnoreExtraPropertiesIfWantedBefore() + { + var text = Lines("bbb: [200,100]", "aaa: hello"); + DeserializerBuilder.IgnoreUnmatchedProperties(); + var actual = Deserializer.Deserialize(UsingReaderFor(text)); + actual.aaa.Should().Be("hello"); + } + + [Fact] + public void IgnoreExtraPropertiesIfWantedNamingScheme() + { + var text = Lines( + "scratch: 'scratcher'", + "deleteScratch: false", + "notScratch: 9443", + "notScratch: 192.168.1.30", + "mappedScratch:", + "- '/work/'" + ); + + DeserializerBuilder + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .IgnoreUnmatchedProperties(); + + var actual = Deserializer.Deserialize(UsingReaderFor(text)); + actual.Scratch.Should().Be("scratcher"); + actual.DeleteScratch.Should().Be(false); + actual.MappedScratch.Should().ContainInOrder(new[] { "/work/" }); + } + + [Fact] + public void InvalidTypeConversionsProduceProperExceptions() + { + var text = Lines("- 1", "- two", "- 3"); + + var sut = new Deserializer(); + var exception = Assert.Throws(() => sut.Deserialize>(UsingReaderFor(text))); + + Assert.Equal(2, exception.Start.Line); + Assert.Equal(3, exception.Start.Column); + } + + [Theory] + [InlineData("blah")] + [InlineData("hello=world")] + [InlineData("+190:20:30")] + [InlineData("x:y")] + public void ValueAllowedAfterDocumentStartToken(string text) + { + var value = Lines("--- " + text); + + var sut = new Deserializer(); + var actual = sut.Deserialize(UsingReaderFor(value)); + + Assert.Equal(text, actual); + } + + [Fact] + public void MappingDisallowedAfterDocumentStartToken() + { + var value = Lines("--- x: y"); + + var sut = new Deserializer(); + var exception = Assert.Throws(() => sut.Deserialize(UsingReaderFor(value))); + + Assert.Equal(1, exception.Start.Line); + Assert.Equal(6, exception.Start.Column); + } + + [Fact] + public void SerializeDynamicPropertyAndApplyNamingConvention() + { + dynamic obj = new ExpandoObject(); + obj.property_one = new ExpandoObject(); + ((IDictionary)obj.property_one).Add("new_key_here", "new_value"); + + var mockNamingConvention = A.Fake(); + A.CallTo(() => mockNamingConvention.Apply(A.Ignored)).Returns("xxx"); + + var serializer = new SerializerBuilder() + .WithNamingConvention(mockNamingConvention) + .Build(); + + var writer = new StringWriter(); + serializer.Serialize(writer, obj); + + writer.ToString().Should().Contain("xxx: new_value"); + } + + [Fact] + public void SerializeGenericDictionaryPropertyAndDoNotApplyNamingConvention() + { + var obj = new Dictionary + { + ["property_one"] = new GenericTestDictionary() + }; + + ((IDictionary)obj["property_one"]).Add("new_key_here", "new_value"); + + var mockNamingConvention = A.Fake(); + A.CallTo(() => mockNamingConvention.Apply(A.Ignored)).Returns("xxx"); + + var serializer = new SerializerBuilder() + .WithNamingConvention(mockNamingConvention) + .Build(); + + var writer = new StringWriter(); + serializer.Serialize(writer, obj); + + writer.ToString().Should().Contain("new_key_here: new_value"); + } + + [Theory, MemberData(nameof(SpecialFloats))] + public void SpecialFloatsAreHandledCorrectly(FloatTestCase testCase) + { + var buffer = new StringWriter(); + Serializer.Serialize(buffer, testCase.Value); + + var firstLine = buffer.ToString().Split('\r', '\n')[0]; + Assert.Equal(testCase.ExpectedTextRepresentation, firstLine); + + var deserializer = new Deserializer(); + var deserializedValue = deserializer.Deserialize(new StringReader(buffer.ToString()), testCase.Value.GetType()); + + Assert.Equal(testCase.Value, deserializedValue); + } + + [Fact] + public void EmptyStringsAreQuoted() + { + var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); + var o = new { test = string.Empty }; + var result = serializer.Serialize(o); + var expected = $"test: \"\"{Environment.NewLine}"; + Assert.Equal(expected, result); + } + + public class FloatTestCase + { + private readonly string description; + public object Value { get; private set; } + public string ExpectedTextRepresentation { get; private set; } + + public FloatTestCase(string description, object value, string expectedTextRepresentation) + { + this.description = description; + Value = value; + ExpectedTextRepresentation = expectedTextRepresentation; + } + + public override string ToString() + { + return description; + } + } + + public static IEnumerable SpecialFloats + { + get + { + return + new[] + { + new FloatTestCase("double.NaN", double.NaN, ".nan"), + new FloatTestCase("double.PositiveInfinity", double.PositiveInfinity, ".inf"), + new FloatTestCase("double.NegativeInfinity", double.NegativeInfinity, "-.inf"), + new FloatTestCase("double.Epsilon", double.Epsilon, double.Epsilon.ToString("G", CultureInfo.InvariantCulture)), + new FloatTestCase("double.26.67", 26.67D, "26.67"), + + new FloatTestCase("float.NaN", float.NaN, ".nan"), + new FloatTestCase("float.PositiveInfinity", float.PositiveInfinity, ".inf"), + new FloatTestCase("float.NegativeInfinity", float.NegativeInfinity, "-.inf"), + new FloatTestCase("float.Epsilon", float.Epsilon, float.Epsilon.ToString("G", CultureInfo.InvariantCulture)), + new FloatTestCase("float.26.67", 26.67F, "26.67"), + +#if NETCOREAPP3_1_OR_GREATER + new FloatTestCase("double.MinValue", double.MinValue, double.MinValue.ToString("G", CultureInfo.InvariantCulture)), + new FloatTestCase("double.MaxValue", double.MaxValue, double.MaxValue.ToString("G", CultureInfo.InvariantCulture)), + new FloatTestCase("float.MinValue", float.MinValue, float.MinValue.ToString("G", CultureInfo.InvariantCulture)), + new FloatTestCase("float.MaxValue", float.MaxValue, float.MaxValue.ToString("G", CultureInfo.InvariantCulture)), +#endif + } + .Select(tc => new object[] { tc }); + } + } + + [Fact] + public void NegativeIntegersCanBeDeserialized() + { + var deserializer = new Deserializer(); + + var value = deserializer.Deserialize(Yaml.ReaderForText(@" + '-123' + ")); + Assert.Equal(-123, value); + } + + [Fact] + public void GenericDictionaryThatDoesNotImplementIDictionaryCanBeDeserialized() + { + var sut = new Deserializer(); + var deserialized = sut.Deserialize>(Yaml.ReaderForText(@" + a: 1 + b: 2 + ")); + + Assert.Equal("1", deserialized["a"]); + Assert.Equal("2", deserialized["b"]); + } + + [Fact] + public void GenericListThatDoesNotImplementIListCanBeDeserialized() + { + var sut = new Deserializer(); + var deserialized = sut.Deserialize>(Yaml.ReaderForText(@" + - a + - b + ")); + + Assert.Contains("a", deserialized); + Assert.Contains("b", deserialized); + } + + [Fact] + public void GuidsShouldBeQuotedWhenSerializedAsJson() + { + var sut = new SerializerBuilder() + .JsonCompatible() + .Build(); + + var yamlAsJson = new StringWriter(); + sut.Serialize(yamlAsJson, new + { + id = Guid.Empty + }); + + Assert.Contains("\"00000000-0000-0000-0000-000000000000\"", yamlAsJson.ToString()); + } + + public class Foo + { + public bool IsRequired { get; set; } + } + + [Fact] + public void AttributeOverridesAndNamingConventionDoNotConflict() + { + var namingConvention = CamelCaseNamingConvention.Instance; + + var yamlMember = new YamlMemberAttribute + { + Alias = "Required" + }; + + var serializer = new SerializerBuilder() + .WithNamingConvention(namingConvention) + .WithAttributeOverride(f => f.IsRequired, yamlMember) + .Build(); + + var yaml = serializer.Serialize(new Foo { IsRequired = true }); + Assert.Contains("required: true", yaml); + + var deserializer = new DeserializerBuilder() + .WithNamingConvention(namingConvention) + .WithAttributeOverride(f => f.IsRequired, yamlMember) + .Build(); + + var deserializedFoo = deserializer.Deserialize(yaml); + Assert.True(deserializedFoo.IsRequired); + } + + [Fact] + public void YamlConvertiblesAreAbleToEmitAndParseComments() + { + var serializer = new Serializer(); + var yaml = serializer.Serialize(new CommentWrapper { Comment = "A comment", Value = "The value" }); + + var deserializer = new Deserializer(); + var parser = new Parser(new Scanner(new StringReader(yaml), skipComments: false)); + var parsed = deserializer.Deserialize>(parser); + + Assert.Equal("A comment", parsed.Comment); + Assert.Equal("The value", parsed.Value); + } + + public class CommentWrapper : IYamlConvertible + { + public string Comment { get; set; } + public T Value { get; set; } + + public void Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) + { + if (parser.TryConsume(out var comment)) + { + Comment = comment.Value; + } + + Value = (T)nestedObjectDeserializer(typeof(T)); + } + + public void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) + { + if (!string.IsNullOrEmpty(Comment)) + { + emitter.Emit(new Comment(Comment, false)); + } + + nestedObjectSerializer(Value, typeof(T)); + } + } + + [Theory] + [InlineData(uint.MinValue)] + [InlineData(uint.MaxValue)] + [InlineData(0x8000000000000000UL)] + public void DeserializationOfUInt64Succeeds(ulong value) + { + var yaml = new Serializer().Serialize(value); + Assert.Contains(value.ToString(), yaml); + + var parsed = new Deserializer().Deserialize(yaml); + Assert.Equal(value, parsed); + } + + [Theory] + [InlineData(int.MinValue)] + [InlineData(int.MaxValue)] + [InlineData(0L)] + public void DeserializationOfInt64Succeeds(long value) + { + var yaml = new Serializer().Serialize(value); + Assert.Contains(value.ToString(), yaml); + + var parsed = new Deserializer().Deserialize(yaml); + Assert.Equal(value, parsed); + } + + public class AnchorsOverwritingTestCase + { + public List a { get; set; } + public List b { get; set; } + public List c { get; set; } + public List d { get; set; } + } + + [Fact] + public void DeserializationOfStreamWithDuplicateAnchorsSucceeds() + { + var yaml = Yaml.ParserForResource("anchors-overwriting.yaml"); + var serializer = new DeserializerBuilder() + .IgnoreUnmatchedProperties() + .Build(); + var deserialized = serializer.Deserialize(yaml); + Assert.NotNull(deserialized); + } + + private sealed class AnchorPrecedence + { + internal sealed class AnchorPrecedenceNested + { + public string b1 { get; set; } + public Dictionary b2 { get; set; } + } + + public string a { get; set; } + public AnchorPrecedenceNested b { get; set; } + public string c { get; set; } + } + + [Fact] + public void DeserializationWithDuplicateAnchorsSucceeds() + { + var sut = new Deserializer(); + var deserialized = sut.Deserialize(@" +a: &anchor1 test0 +b: + b1: &anchor1 test1 + b2: + b21: &anchor1 test2 +c: *anchor1"); + + Assert.Equal("test0", deserialized.a); + Assert.Equal("test1", deserialized.b.b1); + Assert.Contains("b21", deserialized.b.b2.Keys); + Assert.Equal("test2", deserialized.b.b2["b21"]); + Assert.Equal("test2", deserialized.c); + } + + [Fact] + public void SerializeExceptionWithStackTrace() + { + var ex = GetExceptionWithStackTrace(); + var serializer = new SerializerBuilder() + .WithTypeConverter(new MethodInfoConverter()) + .Build(); + var yaml = serializer.Serialize(ex); + Assert.Contains("GetExceptionWithStackTrace", yaml); + } + + private class MethodInfoConverter : IYamlTypeConverter + { + public bool Accepts(Type type) + { + return typeof(MethodInfo).IsAssignableFrom(type); + } + + public object ReadYaml(IParser parser, Type type) + { + throw new NotImplementedException(); + } + + public void WriteYaml(IEmitter emitter, object value, Type type) + { + var method = (MethodInfo)value; + emitter.Emit(new Scalar(string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name))); + } + } + + static Exception GetExceptionWithStackTrace() + { + try + { + throw new ArgumentNullException("foo"); + } + catch (Exception ex) + { + return ex; + } + } + + [Fact] + public void RegisteringATypeConverterPreventsTheTypeFromBeingVisited() + { + var serializer = new SerializerBuilder() + .WithTypeConverter(new NonSerializableTypeConverter()) + .Build(); + + var yaml = serializer.Serialize(new NonSerializableContainer + { + Value = new NonSerializable { Text = "hello" }, + }); + + var deserializer = new DeserializerBuilder() + .WithTypeConverter(new NonSerializableTypeConverter()) + .Build(); + + var result = deserializer.Deserialize(yaml); + + Assert.Equal("hello", result.Value.Text); + } + + [Fact] + public void NamingConventionIsNotAppliedBySerializerWhenApplyNamingConventionsIsFalse() + { + var sut = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build(); + + var yaml = sut.Serialize(new NamingConventionDisabled { NoConvention = "value" }); + + Assert.Contains("NoConvention", yaml); + } + + [Fact] + public void NamingConventionIsNotAppliedByDeserializerWhenApplyNamingConventionsIsFalse() + { + var sut = new DeserializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build(); + + var yaml = "NoConvention: value"; + + var parsed = sut.Deserialize(yaml); + + Assert.Equal("value", parsed.NoConvention); + } + + [Fact] + public void TypesAreSerializable() + { + var sut = new SerializerBuilder() + .Build(); + + var yaml = sut.Serialize(typeof(string)); + + Assert.Contains(typeof(string).AssemblyQualifiedName, yaml); + } + + [Fact] + public void TypesAreDeserializable() + { + var sut = new DeserializerBuilder() + .Build(); + + var type = sut.Deserialize(typeof(string).AssemblyQualifiedName); + + Assert.Equal(typeof(string), type); + } + + [Fact] + public void TypesAreConvertedWhenNeededFromScalars() + { + var sut = new DeserializerBuilder() + .WithTagMapping("!dbl", typeof(DoublyConverted)) + .Build(); + + var result = sut.Deserialize("!dbl hello"); + + Assert.Equal(5, result); + } + + [Fact] + public void TypesAreConvertedWhenNeededInsideLists() + { + var sut = new DeserializerBuilder() + .WithTagMapping("!dbl", typeof(DoublyConverted)) + .Build(); + + var result = sut.Deserialize>("- !dbl hello"); + + Assert.Equal(5, result[0]); + } + + [Fact] + public void TypesAreConvertedWhenNeededInsideDictionary() + { + var sut = new DeserializerBuilder() + .WithTagMapping("!dbl", typeof(DoublyConverted)) + .Build(); + + var result = sut.Deserialize>("!dbl hello: !dbl you"); + + Assert.True(result.ContainsKey(5)); + Assert.Equal(3, result[5]); + } + + [Fact] + public void InfiniteRecursionIsDetected() + { + var sut = new SerializerBuilder() + .DisableAliases() + .Build(); + + var recursionRoot = new + { + Nested = new[] + { + new Dictionary() + } + }; + + recursionRoot.Nested[0].Add("loop", recursionRoot); + + var exception = Assert.Throws(() => sut.Serialize(recursionRoot)); + } + + [Fact] + public void TuplesAreSerializable() + { + var sut = new SerializerBuilder() + .Build(); + + var yaml = sut.Serialize(new[] + { + Tuple.Create(1, "one"), + Tuple.Create(2, "two"), + }); + + var expected = Yaml.Text(@" + - Item1: 1 + Item2: one + - Item1: 2 + Item2: two + "); + + Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); + } + + [Fact] + public void ValueTuplesAreSerializableWithoutMetadata() + { + var sut = new SerializerBuilder() + .Build(); + + var yaml = sut.Serialize(new[] + { + (num: 1, txt: "one"), + (num: 2, txt: "two"), + }); + + var expected = Yaml.Text(@" + - Item1: 1 + Item2: one + - Item1: 2 + Item2: two + "); + + Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); + } + + [Fact] + public void AnchorNameWithTrailingColonReferencedInKeyCanBeDeserialized() + { + var sut = new Deserializer(); + var deserialized = sut.Deserialize>(Yaml.ReaderForText(@" + a: &::::scaryanchor:::: anchor "" value "" + *::::scaryanchor::::: 2 + myvalue: *::::scaryanchor:::: + ")); + + Assert.Equal(@"anchor "" value """, deserialized["a"]); + Assert.Equal("2", deserialized[@"anchor "" value """]); + Assert.Equal(@"anchor "" value """, deserialized["myvalue"]); + } + + [Fact] + public void AliasBeforeAnchorCannotBeDeserialized() + { + var sut = new Deserializer(); + Action action = () => sut.Deserialize>(@" +a: *anchor1 +b: &anchor1 test0 +c: *anchor1"); + + action.ShouldThrow(); + } + + [Fact] + public void AnchorWithAllowedCharactersCanBeDeserialized() + { + var sut = new Deserializer(); + var deserialized = sut.Deserialize>(Yaml.ReaderForText(@" + a: &@nchor<>""@-_123$>>>😁🎉🐻🍔end some value + myvalue: my *@nchor<>""@-_123$>>>😁🎉🐻🍔end test + interpolated value: *@nchor<>""@-_123$>>>😁🎉🐻🍔end + ")); + + Assert.Equal("some value", deserialized["a"]); + Assert.Equal(@"my *@nchor<>""@-_123$>>>😁🎉🐻🍔end test", deserialized["myvalue"]); + Assert.Equal("some value", deserialized["interpolated value"]); + } + + [Fact] + public void SerializationNonPublicPropertiesAreIgnored() + { + var sut = new SerializerBuilder().Build(); + var yaml = sut.Serialize(new NonPublicPropertiesExample()); + Assert.Equal("Public: public", yaml.TrimNewLines()); + } + + [Fact] + public void SerializationNonPublicPropertiesAreIncluded() + { + var sut = new SerializerBuilder().IncludeNonPublicProperties().Build(); + var yaml = sut.Serialize(new NonPublicPropertiesExample()); + + var expected = Yaml.Text(@" + Public: public + Internal: internal + Protected: protected + Private: private + "); + + Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); + } + + [Fact] + public void DeserializationNonPublicPropertiesAreIgnored() + { + var sut = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); + var deserialized = sut.Deserialize(Yaml.ReaderForText(@" + Public: public2 + Internal: internal2 + Protected: protected2 + Private: private2 + ")); + + Assert.Equal("public2,internal,protected,private", deserialized.ToString()); + } + + [Fact] + public void DeserializationNonPublicPropertiesAreIncluded() + { + var sut = new DeserializerBuilder().IncludeNonPublicProperties().Build(); + var deserialized = sut.Deserialize(Yaml.ReaderForText(@" + Public: public2 + Internal: internal2 + Protected: protected2 + Private: private2 + ")); + + Assert.Equal("public2,internal2,protected2,private2", deserialized.ToString()); + } + + [Fact] + public void SerializationNonPublicFieldsAreIgnored() + { + var sut = new SerializerBuilder().Build(); + var yaml = sut.Serialize(new NonPublicFieldsExample()); + Assert.Equal("Public: public", yaml.TrimNewLines()); + } + + [Fact] + public void DeserializationNonPublicFieldsAreIgnored() + { + var sut = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); + var deserialized = sut.Deserialize(Yaml.ReaderForText(@" + Public: public2 + Internal: internal2 + Protected: protected2 + Private: private2 + ")); + + Assert.Equal("public2,internal,protected,private", deserialized.ToString()); + } + + [Fact] + public void ShouldNotIndentSequences() + { + var sut = new SerializerBuilder() + .Build(); + + var yaml = sut.Serialize(new + { + first = "first", + items = new[] + { + "item1", + "item2" + }, + nested = new[] + { + new + { + name = "name1", + more = new[] + { + "nested1", + "nested2" + } + } + } + }); + + var expected = Yaml.Text(@" + first: first + items: + - item1 + - item2 + nested: + - name: name1 + more: + - nested1 + - nested2 + "); + + Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); + } + + [Fact] + public void ShouldIndentSequences() + { + var sut = new SerializerBuilder() + .WithIndentedSequences() + .Build(); + + var yaml = sut.Serialize(new + { + first = "first", + items = new[] + { + "item1", + "item2" + }, + nested = new[] + { + new + { + name = "name1", + more = new[] + { + "nested1", + "nested2" + } + } + } + }); + + var expected = Yaml.Text(@" + first: first + items: + - item1 + - item2 + nested: + - name: name1 + more: + - nested1 + - nested2 + "); + + Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); + } + + [Fact] + public void ExampleFromSpecificationIsHandledCorrectlyWithLateDefine() + { + var parser = new MergingParser(Yaml.ParserForText(@" + # All the following maps are equal: + results: + - # Explicit keys + x: 1 + y: 2 + r: 10 + label: center/big + + - # Merge one map + << : *CENTER + r: 10 + label: center/big + + - # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + + - # Override + << : [ *BIG, *LEFT, *SMALL ] + x: 1 + label: center/big + + obj: + - &CENTER { x: 1, y: 2 } + - &LEFT { x: 0, y: 2 } + - &SMALL { r: 1 } + - &BIG { r: 10 } + ")); + + var result = Deserializer.Deserialize>>>(parser); + + int index = 0; + foreach (var mapping in result["results"]) + { + mapping.Should() + .Contain("x", "1", "'x' should be '1' in result #{0}", index) + .And.Contain("y", "2", "'y' should be '2' in result #{0}", index) + .And.Contain("r", "10", "'r' should be '10' in result #{0}", index) + .And.Contain("label", "center/big", "'label' should be 'center/big' in result #{0}", index); + + ++index; + } + } + + public class CycleTestEntity + { + public CycleTestEntity Cycle { get; set; } + } + + [Fact] + public void SerializeCycleWithAlias() + { + var sut = new SerializerBuilder() + .WithTagMapping("!CycleTag", typeof(CycleTestEntity)) + .Build(); + + var entity = new CycleTestEntity(); + entity.Cycle = entity; + var yaml = sut.Serialize(entity); + var expected = Yaml.Text(@"&o0 !CycleTag +Cycle: *o0"); + + Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines()); + } + + [Fact] + public void DeserializeCycleWithAlias() + { + var sut = new DeserializerBuilder() + .WithTagMapping("!CycleTag", typeof(CycleTestEntity)) + .Build(); + + var yaml = Yaml.Text(@"&o0 !CycleTag +Cycle: *o0"); + var obj = sut.Deserialize(yaml); + + Assert.Same(obj, obj.Cycle); + } + + [Fact] + public void DeserializeCycleWithoutAlias() + { + var sut = new DeserializerBuilder() + .Build(); + + var yaml = Yaml.Text(@"&o0 +Cycle: *o0"); + var obj = sut.Deserialize(yaml); + + Assert.Same(obj, obj.Cycle); + } + + public static IEnumerable Depths => Enumerable.Range(1, 10).Select(i => new[] { (object)i }); + + [Theory] + [MemberData(nameof(Depths))] + public void DeserializeCycleWithAnchorsWithDepth(int? depth) + { + var sut = new DeserializerBuilder() + .WithTagMapping("!CycleTag", typeof(CycleTestEntity)) + .Build(); + + StringBuilder builder = new StringBuilder(@"&o0 !CycleTag"); + builder.AppendLine(); + string indentation; + for (int i = 0; i < depth - 1; ++i) + { + indentation = string.Concat(Enumerable.Repeat(" ", i)); + builder.AppendLine($"{indentation}Cycle: !CycleTag"); + } + indentation = string.Concat(Enumerable.Repeat(" ", depth.Value - 1)); + builder.AppendLine($"{indentation}Cycle: *o0"); + var yaml = Yaml.Text(builder.ToString()); + var obj = sut.Deserialize(yaml); + CycleTestEntity iterator = obj; + for (int i = 0; i < depth; ++i) + { + iterator = iterator.Cycle; + } + Assert.Same(obj, iterator); + } + + [Fact] + public void RoundtripWindowsNewlines() + { + var text = $"Line1{Environment.NewLine}Line2{Environment.NewLine}Line3{Environment.NewLine}{Environment.NewLine}Line4"; + + var sut = new SerializerBuilder().Build(); + var dut = new DeserializerBuilder().Build(); + + using var writer = new StringWriter { NewLine = Environment.NewLine }; + sut.Serialize(writer, new StringContainer { Text = text }); + var serialized = writer.ToString(); + + using var reader = new StringReader(serialized); + var roundtrippedText = dut.Deserialize(reader).Text.NormalizeNewLines(); + Assert.Equal(text, roundtrippedText); + } + + [Theory] + [InlineData("NULL")] + [InlineData("Null")] + [InlineData("null")] + [InlineData("~")] + [InlineData("true")] + [InlineData("false")] + [InlineData("True")] + [InlineData("False")] + [InlineData("TRUE")] + [InlineData("FALSE")] + [InlineData("0o77")] + [InlineData("0x7A")] + [InlineData("+1e10")] + [InlineData("1E10")] + [InlineData("+.inf")] + [InlineData("-.inf")] + [InlineData(".inf")] + [InlineData(".nan")] + [InlineData(".NaN")] + [InlineData(".NAN")] + public void StringsThatMatchKeywordsAreQuoted(string input) + { + var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); + var o = new { text = input }; + var yaml = serializer.Serialize(o); + Assert.Equal($"text: \"{input}\"{Environment.NewLine}", yaml); + } + + [Fact] + public void KeysOnConcreteClassDontGetQuoted_TypeStringGetsQuoted() + { + var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); + var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); + var yaml = @" +True: null +False: hello +Null: true +"; + var obj = deserializer.Deserialize>(yaml); + var result = serializer.Serialize(obj); + obj.True.Should().BeNull(); + obj.False.Should().Be("hello"); + obj.Null.Should().Be("true"); + result.Should().Be($"True: {Environment.NewLine}False: hello{Environment.NewLine}Null: \"true\"{Environment.NewLine}"); + } + + [Fact] + public void KeysOnConcreteClassDontGetQuoted_TypeBoolDoesNotGetQuoted() + { + var serializer = new SerializerBuilder().WithQuotingNecessaryStrings().Build(); + var deserializer = new DeserializerBuilder().WithAttemptingUnquotedStringTypeDeserialization().Build(); + var yaml = @" +True: null +False: hello +Null: true +"; + var obj = deserializer.Deserialize>(yaml); + var result = serializer.Serialize(obj); + obj.True.Should().BeNull(); + obj.False.Should().Be("hello"); + obj.Null.Should().BeTrue(); + result.Should().Be($"True: {Environment.NewLine}False: hello{Environment.NewLine}Null: true{Environment.NewLine}"); + } + + public class ReservedWordsTestClass + { + public string True { get; set; } + public string False { get; set; } + public TNullType Null { get; set; } + } + + [TypeConverter(typeof(DoublyConvertedTypeConverter))] + public class DoublyConverted + { + public string Value { get; set; } + } + + public class DoublyConvertedTypeConverter : TypeConverter + { + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return destinationType == typeof(int); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + return ((DoublyConverted)value).Value.Length; + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + return new DoublyConverted { Value = (string)value }; + } + } + + public class NamingConventionDisabled + { + [YamlMember(ApplyNamingConventions = false)] + public string NoConvention { get; set; } + } + + public class NonSerializableContainer + { + public NonSerializable Value { get; set; } + } + + public class NonSerializable + { + public string WillThrow { get { throw new Exception(); } } + + public string Text { get; set; } + } + + public class StringContainer + { + public string Text { get; set; } + } + + public class NonSerializableTypeConverter : IYamlTypeConverter + { + public bool Accepts(Type type) + { + return typeof(NonSerializable).IsAssignableFrom(type); + } + + public object ReadYaml(IParser parser, Type type) + { + var scalar = parser.Consume(); + return new NonSerializable { Text = scalar.Value }; + } + + public void WriteYaml(IEmitter emitter, object value, Type type) + { + emitter.Emit(new Scalar(((NonSerializable)value).Text)); + } + } + } +} diff --git a/YamlDotNet.Test/Serialization/TypeConverterTests.cs b/YamlDotNet.Test/Serialization/TypeConverterTests.cs index 531e7fcb8..403233d2d 100644 --- a/YamlDotNet.Test/Serialization/TypeConverterTests.cs +++ b/YamlDotNet.Test/Serialization/TypeConverterTests.cs @@ -1,75 +1,75 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Xunit; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Test.Serialization -{ - public class TypeConverterTests - { - public class ImplicitConversionIntWrapper - { - public readonly int value; - - public ImplicitConversionIntWrapper(int value) - { - this.value = value; - } - - public static implicit operator int(ImplicitConversionIntWrapper wrapper) - { - return wrapper.value; - } - } - - public class ExplicitConversionIntWrapper - { - public readonly int value; - - public ExplicitConversionIntWrapper(int value) - { - this.value = value; - } - - public static explicit operator int(ExplicitConversionIntWrapper wrapper) - { - return wrapper.value; - } - } - - [Fact] - public void Implicit_conversion_operator_is_used() - { - var data = new ImplicitConversionIntWrapper(2); - var actual = TypeConverter.ChangeType(data); - Assert.Equal(data.value, actual); - } - - [Fact] - public void Explicit_conversion_operator_is_used() - { - var data = new ExplicitConversionIntWrapper(2); - var actual = TypeConverter.ChangeType(data); - Assert.Equal(data.value, actual); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using Xunit; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Test.Serialization +{ + public class TypeConverterTests + { + public class ImplicitConversionIntWrapper + { + public readonly int value; + + public ImplicitConversionIntWrapper(int value) + { + this.value = value; + } + + public static implicit operator int(ImplicitConversionIntWrapper wrapper) + { + return wrapper.value; + } + } + + public class ExplicitConversionIntWrapper + { + public readonly int value; + + public ExplicitConversionIntWrapper(int value) + { + this.value = value; + } + + public static explicit operator int(ExplicitConversionIntWrapper wrapper) + { + return wrapper.value; + } + } + + [Fact] + public void Implicit_conversion_operator_is_used() + { + var data = new ImplicitConversionIntWrapper(2); + var actual = TypeConverter.ChangeType(data); + Assert.Equal(data.value, actual); + } + + [Fact] + public void Explicit_conversion_operator_is_used() + { + var data = new ExplicitConversionIntWrapper(2); + var actual = TypeConverter.ChangeType(data); + Assert.Equal(data.value, actual); + } + } +} diff --git a/YamlDotNet.Test/Serialization/YamlCommentTests.cs b/YamlDotNet.Test/Serialization/YamlCommentTests.cs index d3321b477..23689bd0f 100644 --- a/YamlDotNet.Test/Serialization/YamlCommentTests.cs +++ b/YamlDotNet.Test/Serialization/YamlCommentTests.cs @@ -1,269 +1,269 @@ -using System; -using FluentAssertions; -using Xunit; -using Xunit.Abstractions; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.EventEmitters; - -namespace YamlDotNet.Test.Serialization -{ - public class YamlCommentTests - { - protected readonly ITestOutputHelper Output; - public YamlCommentTests(ITestOutputHelper helper) - { - Output = helper; - } - - #region Simple block comments - [Fact] - public void SerializationWithBlockComments() - { - var person = new Person { Name = "PandaTea", Age = 100 }; - - var serializer = new Serializer(); - var result = serializer.Serialize(person); - Output.WriteLine(result); - - var deserializer = new Deserializer(); - Action action = () => deserializer.Deserialize(result); - action.ShouldNotThrow(); - - var lines = SplitByLines(result); - - lines.Should().Contain("# The person's name"); - lines.Should().Contain("# The person's age"); - } - - [Fact] - public void SerializationWithBlockComments_Multiline() - { - var multilineComment = new MultilineComment(); - - var serializer = new Serializer(); - var result = serializer.Serialize(multilineComment); - Output.WriteLine(result); - - var deserializer = new Deserializer(); - Action action = () => deserializer.Deserialize(result); - action.ShouldNotThrow(); - - var lines = SplitByLines(result); - - lines[0].Should().Be("# This"); - lines[1].Should().Be("# is"); - lines[2].Should().Be("# multiline"); - - lines[4].Should().Be("# This is"); - lines[5].Should().Be("# too"); - } - - [Fact] - public void SerializationWithBlockComments_NullValue() - { - var serializer = new Serializer(); - Action action = () => serializer.Serialize(new NullComment()); - action.ShouldNotThrow(); - } - #endregion - - #region Indentation of block comments - [Fact] - public void SerializationWithBlockComments_IndentedInSequence() - { - var person = new Person { Name = "PandaTea", Age = 100 }; - - var serializer = new Serializer(); - var result = serializer.Serialize(new Person[] { person }); - Output.WriteLine(result); - - var deserializer = new Deserializer(); - Action action = () => deserializer.Deserialize(result); - action.ShouldNotThrow(); - - var lines = SplitByLines(result); - var indent = GetIndent(1); - - lines.Should().Contain("- # The person's name"); - lines.Should().Contain(indent + "# The person's age"); - } - - [Fact] - public void SerializationWithBlockComments_IndentedInBlock() - { - var garage = new Garage - { - Car = new Car - { - Owner = new Person { Name = "PandaTea", Age = 100 } - } - }; - - var serializer = new Serializer(); - var result = serializer.Serialize(garage); - Output.WriteLine(result); - - var deserializer = new Deserializer(); - Action action = () => deserializer.Deserialize(result); - action.ShouldNotThrow(); - - var lines = SplitByLines(result); - var indent1 = GetIndent(1); - var indent2 = GetIndent(2); - - lines.Should().Contain(indent1 + "# The car's rightful owner"); - lines.Should().Contain(indent2 + "# The person's name"); - lines.Should().Contain(indent2 + "# The person's age"); - } - - [Fact] - public void SerializationWithBlockComments_IndentedInBlockAndSequence() - { - var garage = new Garage - { - Car = new Car - { - Passengers = new[] - { - new Person { Name = "PandaTea", Age = 100 } - } - } - }; - - var serializer = new Serializer(); - var result = serializer.Serialize(garage); - Output.WriteLine(result); - - var deserializer = new Deserializer(); - Action action = () => deserializer.Deserialize(result); - action.ShouldNotThrow(); - - var lines = SplitByLines(result); - var indent1 = GetIndent(1); - var indent2 = GetIndent(2); - - lines.Should().Contain(indent1 + "# The car's rightful owner"); - lines.Should().Contain(indent1 + "- # The person's name"); - lines.Should().Contain(indent2 + "# The person's age"); - } - #endregion - - #region Flow mapping - [Fact] - public void SerializationWithBlockComments_IndentedInBlockAndSequence_WithFlowMapping() - { - var garage = new Garage - { - Car = new Car - { - Owner = new Person { Name = "Paul", Age = 50 }, - Passengers = new[] - { - new Person { Name = "PandaTea", Age = 100 } - } - } - }; - - var serializer = new SerializerBuilder() - .WithEventEmitter(e => new FlowEmitter(e, typeof(Person))) - .Build(); - var result = serializer.Serialize(garage); - Output.WriteLine(result); - - var deserializer = new Deserializer(); - Action action = () => deserializer.Deserialize(result); - action.ShouldNotThrow(); - - var lines = SplitByLines(result); - var indent1 = GetIndent(1); - - lines.Should().Contain("# The car parked in the garage"); - lines.Should().Contain(indent1 + "# The car's rightful owner"); - result.Should().NotContain("The person's name", "because the person's properties are inside of a flow map now"); - result.Should().NotContain("The person's age", "because the person's properties are inside of a flow map now"); - } - - /// - /// This emits objects of given types as flow mappings - /// - public class FlowEmitter : ChainedEventEmitter - { - private readonly Type[] types; - - public FlowEmitter(IEventEmitter nextEmitter, params Type[] types) : base(nextEmitter) - { - this.types = types; - } - - public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) - { - foreach (var type in types) - { - if (eventInfo.Source.Type == type) - { - eventInfo.Style = MappingStyle.Flow; - break; - } - } - base.Emit(eventInfo, emitter); - } - } - #endregion - - class Person - { - [YamlMember(Description = "The person's name")] - public string Name { get; set; } - [YamlMember(Description = "The person's age")] - public int Age { get; set; } - } - - class Car - { - [YamlMember(Description = "The car's rightful owner")] - public Person Owner { get; set; } - public Person[] Passengers { get; set; } - } - - class Garage - { - [YamlMember(Description = "The car parked in the garage")] - public Car Car; - } - - class NullComment - { - [YamlMember(Description = null)] - public int Foo { get; set; } - } - - class MultilineComment - { - [YamlMember(Description = "This\nis\nmultiline")] - public int Foo { get; set; } - - [YamlMember(Description = @"This is -too")] - public int Bar { get; set; } - } - - private static string[] SplitByLines(string result) - { - return result.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); - } - - private static string GetIndent(int depth) - { - var indentWidth = EmitterSettings.Default.BestIndent; - var indent = ""; - while (indent.Length < indentWidth * depth) - { - indent += " "; - } - - return indent; - } - } -} +using System; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.EventEmitters; + +namespace YamlDotNet.Test.Serialization +{ + public class YamlCommentTests + { + protected readonly ITestOutputHelper Output; + public YamlCommentTests(ITestOutputHelper helper) + { + Output = helper; + } + + #region Simple block comments + [Fact] + public void SerializationWithBlockComments() + { + var person = new Person { Name = "PandaTea", Age = 100 }; + + var serializer = new Serializer(); + var result = serializer.Serialize(person); + Output.WriteLine(result); + + var deserializer = new Deserializer(); + Action action = () => deserializer.Deserialize(result); + action.ShouldNotThrow(); + + var lines = SplitByLines(result); + + lines.Should().Contain("# The person's name"); + lines.Should().Contain("# The person's age"); + } + + [Fact] + public void SerializationWithBlockComments_Multiline() + { + var multilineComment = new MultilineComment(); + + var serializer = new Serializer(); + var result = serializer.Serialize(multilineComment); + Output.WriteLine(result); + + var deserializer = new Deserializer(); + Action action = () => deserializer.Deserialize(result); + action.ShouldNotThrow(); + + var lines = SplitByLines(result); + + lines[0].Should().Be("# This"); + lines[1].Should().Be("# is"); + lines[2].Should().Be("# multiline"); + + lines[4].Should().Be("# This is"); + lines[5].Should().Be("# too"); + } + + [Fact] + public void SerializationWithBlockComments_NullValue() + { + var serializer = new Serializer(); + Action action = () => serializer.Serialize(new NullComment()); + action.ShouldNotThrow(); + } + #endregion + + #region Indentation of block comments + [Fact] + public void SerializationWithBlockComments_IndentedInSequence() + { + var person = new Person { Name = "PandaTea", Age = 100 }; + + var serializer = new Serializer(); + var result = serializer.Serialize(new Person[] { person }); + Output.WriteLine(result); + + var deserializer = new Deserializer(); + Action action = () => deserializer.Deserialize(result); + action.ShouldNotThrow(); + + var lines = SplitByLines(result); + var indent = GetIndent(1); + + lines.Should().Contain("- # The person's name"); + lines.Should().Contain(indent + "# The person's age"); + } + + [Fact] + public void SerializationWithBlockComments_IndentedInBlock() + { + var garage = new Garage + { + Car = new Car + { + Owner = new Person { Name = "PandaTea", Age = 100 } + } + }; + + var serializer = new Serializer(); + var result = serializer.Serialize(garage); + Output.WriteLine(result); + + var deserializer = new Deserializer(); + Action action = () => deserializer.Deserialize(result); + action.ShouldNotThrow(); + + var lines = SplitByLines(result); + var indent1 = GetIndent(1); + var indent2 = GetIndent(2); + + lines.Should().Contain(indent1 + "# The car's rightful owner"); + lines.Should().Contain(indent2 + "# The person's name"); + lines.Should().Contain(indent2 + "# The person's age"); + } + + [Fact] + public void SerializationWithBlockComments_IndentedInBlockAndSequence() + { + var garage = new Garage + { + Car = new Car + { + Passengers = new[] + { + new Person { Name = "PandaTea", Age = 100 } + } + } + }; + + var serializer = new Serializer(); + var result = serializer.Serialize(garage); + Output.WriteLine(result); + + var deserializer = new Deserializer(); + Action action = () => deserializer.Deserialize(result); + action.ShouldNotThrow(); + + var lines = SplitByLines(result); + var indent1 = GetIndent(1); + var indent2 = GetIndent(2); + + lines.Should().Contain(indent1 + "# The car's rightful owner"); + lines.Should().Contain(indent1 + "- # The person's name"); + lines.Should().Contain(indent2 + "# The person's age"); + } + #endregion + + #region Flow mapping + [Fact] + public void SerializationWithBlockComments_IndentedInBlockAndSequence_WithFlowMapping() + { + var garage = new Garage + { + Car = new Car + { + Owner = new Person { Name = "Paul", Age = 50 }, + Passengers = new[] + { + new Person { Name = "PandaTea", Age = 100 } + } + } + }; + + var serializer = new SerializerBuilder() + .WithEventEmitter(e => new FlowEmitter(e, typeof(Person))) + .Build(); + var result = serializer.Serialize(garage); + Output.WriteLine(result); + + var deserializer = new Deserializer(); + Action action = () => deserializer.Deserialize(result); + action.ShouldNotThrow(); + + var lines = SplitByLines(result); + var indent1 = GetIndent(1); + + lines.Should().Contain("# The car parked in the garage"); + lines.Should().Contain(indent1 + "# The car's rightful owner"); + result.Should().NotContain("The person's name", "because the person's properties are inside of a flow map now"); + result.Should().NotContain("The person's age", "because the person's properties are inside of a flow map now"); + } + + /// + /// This emits objects of given types as flow mappings + /// + public class FlowEmitter : ChainedEventEmitter + { + private readonly Type[] types; + + public FlowEmitter(IEventEmitter nextEmitter, params Type[] types) : base(nextEmitter) + { + this.types = types; + } + + public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) + { + foreach (var type in types) + { + if (eventInfo.Source.Type == type) + { + eventInfo.Style = MappingStyle.Flow; + break; + } + } + base.Emit(eventInfo, emitter); + } + } + #endregion + + class Person + { + [YamlMember(Description = "The person's name")] + public string Name { get; set; } + [YamlMember(Description = "The person's age")] + public int Age { get; set; } + } + + class Car + { + [YamlMember(Description = "The car's rightful owner")] + public Person Owner { get; set; } + public Person[] Passengers { get; set; } + } + + class Garage + { + [YamlMember(Description = "The car parked in the garage")] + public Car Car; + } + + class NullComment + { + [YamlMember(Description = null)] + public int Foo { get; set; } + } + + class MultilineComment + { + [YamlMember(Description = "This\nis\nmultiline")] + public int Foo { get; set; } + + [YamlMember(Description = @"This is +too")] + public int Bar { get; set; } + } + + private static string[] SplitByLines(string result) + { + return result.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); + } + + private static string GetIndent(int depth) + { + var indentWidth = EmitterSettings.Default.BestIndent; + var indent = ""; + while (indent.Length < indentWidth * depth) + { + indent += " "; + } + + return indent; + } + } +} diff --git a/YamlDotNet.Test/Spec/ParserSpecTests.cs b/YamlDotNet.Test/Spec/ParserSpecTests.cs index 2f8dc12c3..0ab1561a9 100644 --- a/YamlDotNet.Test/Spec/ParserSpecTests.cs +++ b/YamlDotNet.Test/Spec/ParserSpecTests.cs @@ -1,117 +1,117 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using Xunit; -using Xunit.Sdk; -using YamlDotNet.Core; -using YamlDotNet.RepresentationModel; - -namespace YamlDotNet.Test.Spec -{ - public sealed class ParserSpecTests - { - private sealed class ParserSpecTestsData : SpecTestsData - { - protected override List IgnoredSuites { get; } = ignoredSuites; - } - - private static readonly List ignoredSuites = new List - { - "L383" // this is a multi document test, yamldotnet does not support it. - }; - - private static readonly List knownFalsePositives = new List - { - // no false-positives known as of https://github.com/yaml/yaml-test-suite/releases/tag/data-2020-02-11 - }; - - private static readonly List knownParserDesyncInErrorCases = new List - { - "C2SP" // this is supposed to error out, which it does, just not in the same spot. - }; - - [Theory, ClassData(typeof(ParserSpecTestsData))] -#pragma warning disable xUnit1026 // Theory methods should use all of their parameters - public void ConformsWithYamlSpec(string name, string description, string inputFile, string expectedEventFile, bool error, bool quoting) -#pragma warning restore xUnit1026 // Theory methods should use all of their parameters - { - // we don't care about quoting for this test. - - var expectedResult = File.ReadAllText(expectedEventFile) - .Replace("+MAP {}", "+MAP") - .Replace("+SEQ []", "+SEQ"); - - using var writer = new StringWriter(); - try - { - using var reader = File.OpenText(inputFile); - new LibYamlEventStream(new Parser(reader)).WriteTo(writer); - } - catch (Exception ex) - { - Assert.True(error, $"Unexpected spec failure ({name}).\n{description}\nExpected:\n{expectedResult}\nActual:\n[Writer Output]\n{writer}\n[Exception]\n{ex}"); - - if (error) - { - Debug.Assert(!knownFalsePositives.Contains(name), $"Spec test '{name}' passed but present in '{nameof(knownFalsePositives)}' list. Consider removing it from the list."); - - try - { - Assert.Equal(expectedResult, writer.ToString(), ignoreLineEndingDifferences: true); - Debug.Assert(!knownParserDesyncInErrorCases.Contains(name), $"Spec test '{name}' passed but present in '{nameof(knownParserDesyncInErrorCases)}' list. Consider removing it from the list."); - } - catch (EqualException) - { - // In some error cases, YamlDotNet's parser output is in desync with what is expected by the spec. - // Throw, if it is not a known case. - - if (!knownParserDesyncInErrorCases.Contains(name)) - { - throw; - } - } - } - - return; - } - - try - { - Assert.Equal(expectedResult, writer.ToString(), ignoreLineEndingDifferences: true); - Debug.Assert(!ignoredSuites.Contains(name), $"Spec test '{name}' passed but present in '{nameof(ignoredSuites)}' list. Consider removing it from the list."); - } - catch (EqualException) - { - // In some cases, YamlDotNet's parser/scanner is unexpectedly *not* erroring out. - // Throw, if it is not a known case. - - if (!(error && knownFalsePositives.Contains(name))) - { - throw; - } - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Xunit; +using Xunit.Sdk; +using YamlDotNet.Core; +using YamlDotNet.RepresentationModel; + +namespace YamlDotNet.Test.Spec +{ + public sealed class ParserSpecTests + { + private sealed class ParserSpecTestsData : SpecTestsData + { + protected override List IgnoredSuites { get; } = ignoredSuites; + } + + private static readonly List ignoredSuites = new List + { + "L383" // this is a multi document test, yamldotnet does not support it. + }; + + private static readonly List knownFalsePositives = new List + { + // no false-positives known as of https://github.com/yaml/yaml-test-suite/releases/tag/data-2020-02-11 + }; + + private static readonly List knownParserDesyncInErrorCases = new List + { + "C2SP" // this is supposed to error out, which it does, just not in the same spot. + }; + + [Theory, ClassData(typeof(ParserSpecTestsData))] +#pragma warning disable xUnit1026 // Theory methods should use all of their parameters + public void ConformsWithYamlSpec(string name, string description, string inputFile, string expectedEventFile, bool error, bool quoting) +#pragma warning restore xUnit1026 // Theory methods should use all of their parameters + { + // we don't care about quoting for this test. + + var expectedResult = File.ReadAllText(expectedEventFile) + .Replace("+MAP {}", "+MAP") + .Replace("+SEQ []", "+SEQ"); + + using var writer = new StringWriter(); + try + { + using var reader = File.OpenText(inputFile); + new LibYamlEventStream(new Parser(reader)).WriteTo(writer); + } + catch (Exception ex) + { + Assert.True(error, $"Unexpected spec failure ({name}).\n{description}\nExpected:\n{expectedResult}\nActual:\n[Writer Output]\n{writer}\n[Exception]\n{ex}"); + + if (error) + { + Debug.Assert(!knownFalsePositives.Contains(name), $"Spec test '{name}' passed but present in '{nameof(knownFalsePositives)}' list. Consider removing it from the list."); + + try + { + Assert.Equal(expectedResult, writer.ToString(), ignoreLineEndingDifferences: true); + Debug.Assert(!knownParserDesyncInErrorCases.Contains(name), $"Spec test '{name}' passed but present in '{nameof(knownParserDesyncInErrorCases)}' list. Consider removing it from the list."); + } + catch (EqualException) + { + // In some error cases, YamlDotNet's parser output is in desync with what is expected by the spec. + // Throw, if it is not a known case. + + if (!knownParserDesyncInErrorCases.Contains(name)) + { + throw; + } + } + } + + return; + } + + try + { + Assert.Equal(expectedResult, writer.ToString(), ignoreLineEndingDifferences: true); + Debug.Assert(!ignoredSuites.Contains(name), $"Spec test '{name}' passed but present in '{nameof(ignoredSuites)}' list. Consider removing it from the list."); + } + catch (EqualException) + { + // In some cases, YamlDotNet's parser/scanner is unexpectedly *not* erroring out. + // Throw, if it is not a known case. + + if (!(error && knownFalsePositives.Contains(name))) + { + throw; + } + } + } + } +} diff --git a/YamlDotNet.Test/Spec/SerializerSpecTests.cs b/YamlDotNet.Test/Spec/SerializerSpecTests.cs index 8b69240f2..06185ade1 100644 --- a/YamlDotNet.Test/Spec/SerializerSpecTests.cs +++ b/YamlDotNet.Test/Spec/SerializerSpecTests.cs @@ -1,134 +1,134 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using Xunit; -using Xunit.Sdk; -using YamlDotNet.Serialization; - -namespace YamlDotNet.Test.Spec -{ - public sealed class SerializerSpecTests - { - internal sealed class SerializerSpecTestsData : SpecTestsData - { - protected override List IgnoredSuites { get; } = ignoredSuites; - } - - private static readonly List ignoredSuites = new List - { - //TODO: research why these are ignored - "26DV", "27NA", "2AUY", "2JQS", "2LFX", "2SXE", "2XXW", "33X3", "35KP", "36F6", "3GZX", "3MYT", "3R3P", "3UYS", "4ABK", - "4CQQ", "4FJ6", "4GC6", "4MUZ", "4Q9F", "4QFQ", "4UYU", "4V8U", "4ZYM", "52DL", "565N", "57H4", "5BVJ", "5GBF", "5MUD", - "5TYM", "5WE3", "6BFJ", "6CK3", "6FWR", "6HB6", "6JQW", "6JWB", "6KGN", "6LVF", "6M2F", "6PBE", "6SLA", "6WLZ", "6XDY", - "6ZKB", "735Y", "74H7", "753E", "77H8", "7BMT", "7BUB", "7FWL", "7T8X", "7TMG", "7W2P", "7Z25", "7ZZ5", "87E4", "8CWC", - "8G76", "8KB6", "8MK2", "8UDB", "8XYN", "93WF", "96L6", "98YD", "9BXH", "9DXL", "9KAX", "9MMW", "9SA2", "9SHH", "9U5K", - "9WXW", "9YRD", "A6F9", "AVM7", "BEC7", "BU8L", "C2DT", "C4HZ", "CC74", "CN3R", "CPZ3", "CUP7", "D83L", "DBG4", "DFF7", - "DK3J", "E76Z", "EHF6", "EX5H", "F2C7", "F3CP", "F6MC", "F8F9", "FH7J", "FP8R", "FRK4", "FTA2", "G4RS", "G5U8", "H3Z8", - "HMK4", "HMQ5", "HS5T", "HWV9", "J3BT", "J7PZ", "J9HZ", "JDH8", "JHB9", "JS2J", "JTV5", "K3WX", "K54U", "K858", "KK5P", - "KSS4", "KZN9", "L94M", "LE5A", "LP6E", "LQZ7", "M29M", "M5C3", "M7A3", "M7NX", "M9B4", "MJS9", "MYW6", "MZX3", "NAT4", - "NB6Z", "NHX8", "NJ66", "NP9H", "P2AD", "P76L", "PRH3", "PUW8", "PW8X", "Q88A", "Q8AD", "QT73", "R4YG", "R52L", "RTP8", - "RZP5", "RZT7", "S3PD", "S4JQ", "S4T7", "S7BG", "SKE5", "SSW6", "T4YY", "T5N4", "U3C3", "U3XV", "U9NS", "UGM3", "UT92", - "V55R", "W42U", "W4TN", "W5VH", "WZ62", "X38W", "X8DW", "XLQ9", "XV9V", "XW4D", "Y2GN", "Z67P", "Z9M4", "ZH7C", "ZWK4", - - "L383", "NKF9", // We don't do multiple yaml documents in a single file - "4WA9", // we lose whether the source yaml used folding text so we know it will fail. If B3HG passes, then we're good. - "6CA3", // we lose whether the source yaml was originally tabbed in or not so we can't do this test. - "CFD4", // we don't support null key names - "M6YH", // we lose the folding context (see 4WA9 above) so this test will always fail. - }; - - private static readonly List knownFalsePositives = new List - { - // no false-positives known as of https://github.com/yaml/yaml-test-suite/releases/tag/data-2020-02-11 - }; - - - [Theory, ClassData(typeof(SerializerSpecTestsData))] - public void ConformsWithYamlSpec(string name, string description, string inputFile, string outputFile, bool error, bool quoting) - { - var deserializerBuilder = new DeserializerBuilder(); - var serializerBuilder = new SerializerBuilder(); - - if (quoting) - { - deserializerBuilder.WithAttemptingUnquotedStringTypeDeserialization(); - serializerBuilder.WithQuotingNecessaryStrings(); - } - - var serializer = serializerBuilder.Build(); - var deserializer = deserializerBuilder.Build(); - - var expectedResult = File.ReadAllText(outputFile); - using var writer = new StringWriter(); - try - { - using var reader = File.OpenText(inputFile); - var subject = deserializer.Deserialize(reader); - serializer.Serialize(writer, subject); - } - catch (Exception ex) - { - Assert.True(error, $"Unexpected spec failure ({name}).\n{description}\nExpected:\n{expectedResult}\nActual:\n[Writer Output]\n{writer}\n[Exception]\n{ex}"); - - if (error) - { - Debug.Assert(!knownFalsePositives.Contains(name), $"Spec test '{name}' passed but present in '{nameof(knownFalsePositives)}' list. Consider removing it from the list."); - } - - return; - } - - try - { - // Some of the output files have the document separator at the start, we don't do that, so remove it. - if (expectedResult.StartsWith($"---")) - { - expectedResult = expectedResult.Substring(4); - } - if (expectedResult.StartsWith("\r")) - { - expectedResult = expectedResult.Substring(1); - } - if (expectedResult.StartsWith("\n")) - { - expectedResult = expectedResult.Substring(1); - } - - Assert.Equal(expectedResult, writer.ToString(), ignoreLineEndingDifferences: true); - Debug.Assert(!ignoredSuites.Contains(name), $"Spec test '{name}' passed but present in '{nameof(ignoredSuites)}' list. Consider removing it from the list."); - } - catch (EqualException) - { - // In some cases, YamlDotNet's parser/scanner is unexpectedly *not* erroring out. - // Throw, if it is not a known case. - - if (!(error && knownFalsePositives.Contains(name))) - { - throw; - } - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Xunit; +using Xunit.Sdk; +using YamlDotNet.Serialization; + +namespace YamlDotNet.Test.Spec +{ + public sealed class SerializerSpecTests + { + internal sealed class SerializerSpecTestsData : SpecTestsData + { + protected override List IgnoredSuites { get; } = ignoredSuites; + } + + private static readonly List ignoredSuites = new List + { + //TODO: research why these are ignored + "26DV", "27NA", "2AUY", "2JQS", "2LFX", "2SXE", "2XXW", "33X3", "35KP", "36F6", "3GZX", "3MYT", "3R3P", "3UYS", "4ABK", + "4CQQ", "4FJ6", "4GC6", "4MUZ", "4Q9F", "4QFQ", "4UYU", "4V8U", "4ZYM", "52DL", "565N", "57H4", "5BVJ", "5GBF", "5MUD", + "5TYM", "5WE3", "6BFJ", "6CK3", "6FWR", "6HB6", "6JQW", "6JWB", "6KGN", "6LVF", "6M2F", "6PBE", "6SLA", "6WLZ", "6XDY", + "6ZKB", "735Y", "74H7", "753E", "77H8", "7BMT", "7BUB", "7FWL", "7T8X", "7TMG", "7W2P", "7Z25", "7ZZ5", "87E4", "8CWC", + "8G76", "8KB6", "8MK2", "8UDB", "8XYN", "93WF", "96L6", "98YD", "9BXH", "9DXL", "9KAX", "9MMW", "9SA2", "9SHH", "9U5K", + "9WXW", "9YRD", "A6F9", "AVM7", "BEC7", "BU8L", "C2DT", "C4HZ", "CC74", "CN3R", "CPZ3", "CUP7", "D83L", "DBG4", "DFF7", + "DK3J", "E76Z", "EHF6", "EX5H", "F2C7", "F3CP", "F6MC", "F8F9", "FH7J", "FP8R", "FRK4", "FTA2", "G4RS", "G5U8", "H3Z8", + "HMK4", "HMQ5", "HS5T", "HWV9", "J3BT", "J7PZ", "J9HZ", "JDH8", "JHB9", "JS2J", "JTV5", "K3WX", "K54U", "K858", "KK5P", + "KSS4", "KZN9", "L94M", "LE5A", "LP6E", "LQZ7", "M29M", "M5C3", "M7A3", "M7NX", "M9B4", "MJS9", "MYW6", "MZX3", "NAT4", + "NB6Z", "NHX8", "NJ66", "NP9H", "P2AD", "P76L", "PRH3", "PUW8", "PW8X", "Q88A", "Q8AD", "QT73", "R4YG", "R52L", "RTP8", + "RZP5", "RZT7", "S3PD", "S4JQ", "S4T7", "S7BG", "SKE5", "SSW6", "T4YY", "T5N4", "U3C3", "U3XV", "U9NS", "UGM3", "UT92", + "V55R", "W42U", "W4TN", "W5VH", "WZ62", "X38W", "X8DW", "XLQ9", "XV9V", "XW4D", "Y2GN", "Z67P", "Z9M4", "ZH7C", "ZWK4", + + "L383", "NKF9", // We don't do multiple yaml documents in a single file + "4WA9", // we lose whether the source yaml used folding text so we know it will fail. If B3HG passes, then we're good. + "6CA3", // we lose whether the source yaml was originally tabbed in or not so we can't do this test. + "CFD4", // we don't support null key names + "M6YH", // we lose the folding context (see 4WA9 above) so this test will always fail. + }; + + private static readonly List knownFalsePositives = new List + { + // no false-positives known as of https://github.com/yaml/yaml-test-suite/releases/tag/data-2020-02-11 + }; + + + [Theory, ClassData(typeof(SerializerSpecTestsData))] + public void ConformsWithYamlSpec(string name, string description, string inputFile, string outputFile, bool error, bool quoting) + { + var deserializerBuilder = new DeserializerBuilder(); + var serializerBuilder = new SerializerBuilder(); + + if (quoting) + { + deserializerBuilder.WithAttemptingUnquotedStringTypeDeserialization(); + serializerBuilder.WithQuotingNecessaryStrings(); + } + + var serializer = serializerBuilder.Build(); + var deserializer = deserializerBuilder.Build(); + + var expectedResult = File.ReadAllText(outputFile); + using var writer = new StringWriter(); + try + { + using var reader = File.OpenText(inputFile); + var subject = deserializer.Deserialize(reader); + serializer.Serialize(writer, subject); + } + catch (Exception ex) + { + Assert.True(error, $"Unexpected spec failure ({name}).\n{description}\nExpected:\n{expectedResult}\nActual:\n[Writer Output]\n{writer}\n[Exception]\n{ex}"); + + if (error) + { + Debug.Assert(!knownFalsePositives.Contains(name), $"Spec test '{name}' passed but present in '{nameof(knownFalsePositives)}' list. Consider removing it from the list."); + } + + return; + } + + try + { + // Some of the output files have the document separator at the start, we don't do that, so remove it. + if (expectedResult.StartsWith($"---")) + { + expectedResult = expectedResult.Substring(4); + } + if (expectedResult.StartsWith("\r")) + { + expectedResult = expectedResult.Substring(1); + } + if (expectedResult.StartsWith("\n")) + { + expectedResult = expectedResult.Substring(1); + } + + Assert.Equal(expectedResult, writer.ToString(), ignoreLineEndingDifferences: true); + Debug.Assert(!ignoredSuites.Contains(name), $"Spec test '{name}' passed but present in '{nameof(ignoredSuites)}' list. Consider removing it from the list."); + } + catch (EqualException) + { + // In some cases, YamlDotNet's parser/scanner is unexpectedly *not* erroring out. + // Throw, if it is not a known case. + + if (!(error && knownFalsePositives.Contains(name))) + { + throw; + } + } + } + } +} diff --git a/YamlDotNet.Test/Spec/SpecTestData.cs b/YamlDotNet.Test/Spec/SpecTestData.cs index c616a3e32..b26538066 100644 --- a/YamlDotNet.Test/Spec/SpecTestData.cs +++ b/YamlDotNet.Test/Spec/SpecTestData.cs @@ -1,131 +1,131 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; - -namespace YamlDotNet.Test.Spec -{ - internal abstract class SpecTestsData : IEnumerable - { - private const string DescriptionFilename = "==="; - private const string InputFilename = "in.yaml"; - private const string OutputFilename = "out.yaml"; - private const string ExpectedEventFilename = "test.event"; - private const string ErrorFilename = "error"; - - private static readonly string specFixtureDirectory = GetTestFixtureDirectory(); - - protected abstract List IgnoredSuites { get; } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - public IEnumerator GetEnumerator() - { - var fixtures = Directory.EnumerateDirectories(specFixtureDirectory, "*", SearchOption.TopDirectoryOnly); - - foreach (var testPath in fixtures) - { - var testName = Path.GetFileName(testPath); - // comment the following line to run spec tests (requires 'Rebuild') - if (IgnoredSuites.Contains(testName)) - { - continue; - } - - var inputFile = Path.Combine(testPath, InputFilename); - if (!File.Exists(inputFile)) - { - continue; - } - - var descriptionFile = Path.Combine(testPath, DescriptionFilename); - var hasErrorFile = File.Exists(Path.Combine(testPath, ErrorFilename)); - var expectedEventFile = Path.Combine(testPath, ExpectedEventFilename); - - var outputFile = Path.Combine(testPath, OutputFilename); - if (!File.Exists(outputFile)) - { - outputFile = inputFile; - } - - yield return new object[] - { - testName, - File.ReadAllText(descriptionFile).TrimEnd(), - inputFile, - (this is SerializerSpecTests.SerializerSpecTestsData) ? outputFile : expectedEventFile, - hasErrorFile, - true //quoting enabled - }; - yield return new object[] - { - testName, - File.ReadAllText(descriptionFile).TrimEnd(), - inputFile, - (this is SerializerSpecTests.SerializerSpecTestsData) ? outputFile : expectedEventFile, - hasErrorFile, - false //quoting not enabled - }; - } - } - - private static string GetTestFixtureDirectory() - { - // check if environment variable YAMLDOTNET_SPEC_SUITE_DIR is set - var fixturesPath = Environment.GetEnvironmentVariable("YAMLDOTNET_SPEC_SUITE_DIR"); - - if (!string.IsNullOrEmpty(fixturesPath)) - { - if (!Directory.Exists(fixturesPath)) - { - throw new Exception("Path set as environment variable 'YAMLDOTNET_SPEC_SUITE_DIR' does not exist!"); - } - - return fixturesPath; - } - - // In Microsoft.NET.Test.Sdk v15.0.0, the current working directory - // is not set to project's root but instead the output directory. - // see: https://github.com/Microsoft/vstest/issues/435. - // - // Let's use the strategry of finding the parent directory of - // "yaml-test-suite" directory by walking from cwd backwards upto the - // volume's root. - var currentDirectory = Directory.GetCurrentDirectory(); - var currentDirectoryInfo = new DirectoryInfo(currentDirectory); - - do - { - if (Directory.Exists(Path.Combine(currentDirectoryInfo.FullName, "yaml-test-suite"))) - { - return Path.Combine(currentDirectoryInfo.FullName, "yaml-test-suite"); - } - currentDirectoryInfo = currentDirectoryInfo.Parent; - } - while (currentDirectoryInfo.Parent != null); - - throw new DirectoryNotFoundException("Unable to find 'yaml-test-suite' directory"); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +namespace YamlDotNet.Test.Spec +{ + internal abstract class SpecTestsData : IEnumerable + { + private const string DescriptionFilename = "==="; + private const string InputFilename = "in.yaml"; + private const string OutputFilename = "out.yaml"; + private const string ExpectedEventFilename = "test.event"; + private const string ErrorFilename = "error"; + + private static readonly string specFixtureDirectory = GetTestFixtureDirectory(); + + protected abstract List IgnoredSuites { get; } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public IEnumerator GetEnumerator() + { + var fixtures = Directory.EnumerateDirectories(specFixtureDirectory, "*", SearchOption.TopDirectoryOnly); + + foreach (var testPath in fixtures) + { + var testName = Path.GetFileName(testPath); + // comment the following line to run spec tests (requires 'Rebuild') + if (IgnoredSuites.Contains(testName)) + { + continue; + } + + var inputFile = Path.Combine(testPath, InputFilename); + if (!File.Exists(inputFile)) + { + continue; + } + + var descriptionFile = Path.Combine(testPath, DescriptionFilename); + var hasErrorFile = File.Exists(Path.Combine(testPath, ErrorFilename)); + var expectedEventFile = Path.Combine(testPath, ExpectedEventFilename); + + var outputFile = Path.Combine(testPath, OutputFilename); + if (!File.Exists(outputFile)) + { + outputFile = inputFile; + } + + yield return new object[] + { + testName, + File.ReadAllText(descriptionFile).TrimEnd(), + inputFile, + (this is SerializerSpecTests.SerializerSpecTestsData) ? outputFile : expectedEventFile, + hasErrorFile, + true //quoting enabled + }; + yield return new object[] + { + testName, + File.ReadAllText(descriptionFile).TrimEnd(), + inputFile, + (this is SerializerSpecTests.SerializerSpecTestsData) ? outputFile : expectedEventFile, + hasErrorFile, + false //quoting not enabled + }; + } + } + + private static string GetTestFixtureDirectory() + { + // check if environment variable YAMLDOTNET_SPEC_SUITE_DIR is set + var fixturesPath = Environment.GetEnvironmentVariable("YAMLDOTNET_SPEC_SUITE_DIR"); + + if (!string.IsNullOrEmpty(fixturesPath)) + { + if (!Directory.Exists(fixturesPath)) + { + throw new Exception("Path set as environment variable 'YAMLDOTNET_SPEC_SUITE_DIR' does not exist!"); + } + + return fixturesPath; + } + + // In Microsoft.NET.Test.Sdk v15.0.0, the current working directory + // is not set to project's root but instead the output directory. + // see: https://github.com/Microsoft/vstest/issues/435. + // + // Let's use the strategry of finding the parent directory of + // "yaml-test-suite" directory by walking from cwd backwards upto the + // volume's root. + var currentDirectory = Directory.GetCurrentDirectory(); + var currentDirectoryInfo = new DirectoryInfo(currentDirectory); + + do + { + if (Directory.Exists(Path.Combine(currentDirectoryInfo.FullName, "yaml-test-suite"))) + { + return Path.Combine(currentDirectoryInfo.FullName, "yaml-test-suite"); + } + currentDirectoryInfo = currentDirectoryInfo.Parent; + } + while (currentDirectoryInfo.Parent != null); + + throw new DirectoryNotFoundException("Unable to find 'yaml-test-suite' directory"); + } + } +} diff --git a/YamlDotNet.Test/StringExtensions.cs b/YamlDotNet.Test/StringExtensions.cs index acfb24366..8c7dff623 100644 --- a/YamlDotNet.Test/StringExtensions.cs +++ b/YamlDotNet.Test/StringExtensions.cs @@ -1,40 +1,40 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Test -{ - public static class StringExtensions - { - public static string NormalizeNewLines(this string value) - { - return value - .Replace("\r\n", "\n") - .Replace("\n", Environment.NewLine); - } - - public static string TrimNewLines(this string value) - { - return value.TrimEnd('\r', '\n'); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Test +{ + public static class StringExtensions + { + public static string NormalizeNewLines(this string value) + { + return value + .Replace("\r\n", "\n") + .Replace("\n", Environment.NewLine); + } + + public static string TrimNewLines(this string value) + { + return value.TrimEnd('\r', '\n'); + } + } +} diff --git a/YamlDotNet.Test/Yaml.cs b/YamlDotNet.Test/Yaml.cs index 21c53af75..63b99c054 100644 --- a/YamlDotNet.Test/Yaml.cs +++ b/YamlDotNet.Test/Yaml.cs @@ -1,118 +1,118 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; -using YamlDotNet.Core; - -namespace YamlDotNet.Test -{ - public static class Yaml - { - public static TextReader ReaderFrom(string name) - { - return new StreamReader(StreamFrom(name)); - } - - public static Stream StreamFrom(string name) - { - var fromType = typeof(Yaml); - var assembly = fromType.GetTypeInfo().Assembly; - var stream = assembly.GetManifestResourceStream(name) ?? - assembly.GetManifestResourceStream(fromType.Namespace + ".files." + name); - return stream; - } - - public static string TemplatedOn(this TextReader reader) - { - var text = reader.ReadToEnd(); - return text.TemplatedOn(); - } - - public static string TemplatedOn(this string text) - { - return Regex.Replace(text, @"{type}", match => - Uri.EscapeDataString(typeof(T).Name)); - } - - public static IParser ParserForEmptyContent() - { - return new Parser(new StringReader(string.Empty)); - } - - public static IParser ParserForResource(string name) - { - return new Parser(Yaml.ReaderFrom(name)); - } - - public static IParser ParserForText(string yamlText) - { - return new Parser(ReaderForText(yamlText)); - } - - public static Scanner ScannerForResource(string name) - { - return new Scanner(Yaml.ReaderFrom(name)); - } - - public static Scanner ScannerForText(string yamlText) - { - return new Scanner(ReaderForText(yamlText)); - } - - public static StringReader ReaderForText(string yamlText) - { - return new StringReader(Text(yamlText)); - } - - public static string Text(string yamlText) - { - var lines = yamlText - .Split('\n') - .Select(l => l.TrimEnd('\r', '\n')) - .SkipWhile(l => l.Trim(' ', '\t').Length == 0) - .ToList(); - - while (lines.Count > 0 && lines[lines.Count - 1].Trim(' ', '\t').Length == 0) - { - lines.RemoveAt(lines.Count - 1); - } - - if (lines.Count > 0) - { - var indent = Regex.Match(lines[0], @"^(\s*)"); - if (!indent.Success) - { - throw new ArgumentException("Invalid indentation"); - } - - lines = lines - .Select(l => l.Substring(indent.Groups[1].Length)) - .ToList(); - } - - return string.Join("\n", lines.ToArray()); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using YamlDotNet.Core; + +namespace YamlDotNet.Test +{ + public static class Yaml + { + public static TextReader ReaderFrom(string name) + { + return new StreamReader(StreamFrom(name)); + } + + public static Stream StreamFrom(string name) + { + var fromType = typeof(Yaml); + var assembly = fromType.GetTypeInfo().Assembly; + var stream = assembly.GetManifestResourceStream(name) ?? + assembly.GetManifestResourceStream(fromType.Namespace + ".files." + name); + return stream; + } + + public static string TemplatedOn(this TextReader reader) + { + var text = reader.ReadToEnd(); + return text.TemplatedOn(); + } + + public static string TemplatedOn(this string text) + { + return Regex.Replace(text, @"{type}", match => + Uri.EscapeDataString(typeof(T).Name)); + } + + public static IParser ParserForEmptyContent() + { + return new Parser(new StringReader(string.Empty)); + } + + public static IParser ParserForResource(string name) + { + return new Parser(Yaml.ReaderFrom(name)); + } + + public static IParser ParserForText(string yamlText) + { + return new Parser(ReaderForText(yamlText)); + } + + public static Scanner ScannerForResource(string name) + { + return new Scanner(Yaml.ReaderFrom(name)); + } + + public static Scanner ScannerForText(string yamlText) + { + return new Scanner(ReaderForText(yamlText)); + } + + public static StringReader ReaderForText(string yamlText) + { + return new StringReader(Text(yamlText)); + } + + public static string Text(string yamlText) + { + var lines = yamlText + .Split('\n') + .Select(l => l.TrimEnd('\r', '\n')) + .SkipWhile(l => l.Trim(' ', '\t').Length == 0) + .ToList(); + + while (lines.Count > 0 && lines[lines.Count - 1].Trim(' ', '\t').Length == 0) + { + lines.RemoveAt(lines.Count - 1); + } + + if (lines.Count > 0) + { + var indent = Regex.Match(lines[0], @"^(\s*)"); + if (!indent.Success) + { + throw new ArgumentException("Invalid indentation"); + } + + lines = lines + .Select(l => l.Substring(indent.Groups[1].Length)) + .ToList(); + } + + return string.Join("\n", lines.ToArray()); + } + } +} diff --git a/YamlDotNet/Core/AnchorName.cs b/YamlDotNet/Core/AnchorName.cs index 413ad70e2..92c37c924 100644 --- a/YamlDotNet/Core/AnchorName.cs +++ b/YamlDotNet/Core/AnchorName.cs @@ -1,76 +1,76 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Text.RegularExpressions; - -namespace YamlDotNet.Core -{ - public struct AnchorName : IEquatable - { - public static readonly AnchorName Empty = default; - - // https://yaml.org/spec/1.2/spec.html#id2785586 - private static readonly Regex AnchorPattern = new Regex(@"^[^\[\]\{\},]+$", StandardRegexOptions.Compiled); - - private readonly string? value; - - public string Value => value ?? throw new InvalidOperationException("Cannot read the Value of an empty anchor"); - - public bool IsEmpty => value is null; - - public AnchorName(string value) - { - this.value = value ?? throw new ArgumentNullException(nameof(value)); - - if (!AnchorPattern.IsMatch(value)) - { - throw new ArgumentException($"Anchor cannot be empty or contain disallowed characters: []{{}},\nThe value was '{value}'.", nameof(value)); - } - } - - public override string ToString() => value ?? "[empty]"; - - public bool Equals(AnchorName other) => Equals(value, other.value); - - public override bool Equals(object? obj) - { - return obj is AnchorName other && Equals(other); - } - - public override int GetHashCode() - { - return value?.GetHashCode() ?? 0; - } - - public static bool operator ==(AnchorName left, AnchorName right) - { - return left.Equals(right); - } - - public static bool operator !=(AnchorName left, AnchorName right) - { - return !(left == right); - } - - public static implicit operator AnchorName(string? value) => value == null ? Empty : new AnchorName(value); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Text.RegularExpressions; + +namespace YamlDotNet.Core +{ + public struct AnchorName : IEquatable + { + public static readonly AnchorName Empty = default; + + // https://yaml.org/spec/1.2/spec.html#id2785586 + private static readonly Regex AnchorPattern = new Regex(@"^[^\[\]\{\},]+$", StandardRegexOptions.Compiled); + + private readonly string? value; + + public string Value => value ?? throw new InvalidOperationException("Cannot read the Value of an empty anchor"); + + public bool IsEmpty => value is null; + + public AnchorName(string value) + { + this.value = value ?? throw new ArgumentNullException(nameof(value)); + + if (!AnchorPattern.IsMatch(value)) + { + throw new ArgumentException($"Anchor cannot be empty or contain disallowed characters: []{{}},\nThe value was '{value}'.", nameof(value)); + } + } + + public override string ToString() => value ?? "[empty]"; + + public bool Equals(AnchorName other) => Equals(value, other.value); + + public override bool Equals(object? obj) + { + return obj is AnchorName other && Equals(other); + } + + public override int GetHashCode() + { + return value?.GetHashCode() ?? 0; + } + + public static bool operator ==(AnchorName left, AnchorName right) + { + return left.Equals(right); + } + + public static bool operator !=(AnchorName left, AnchorName right) + { + return !(left == right); + } + + public static implicit operator AnchorName(string? value) => value == null ? Empty : new AnchorName(value); + } +} diff --git a/YamlDotNet/Core/AnchorNotFoundException.cs b/YamlDotNet/Core/AnchorNotFoundException.cs index d9218f7ef..d40337bea 100644 --- a/YamlDotNet/Core/AnchorNotFoundException.cs +++ b/YamlDotNet/Core/AnchorNotFoundException.cs @@ -1,58 +1,58 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - /// - /// The exception that is thrown when an alias references an anchor that does not exist. - /// - public class AnchorNotFoundException : YamlException - { - /// - /// Initializes a new instance of the class. - /// - /// The message. - public AnchorNotFoundException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class. - /// - public AnchorNotFoundException(in Mark start, in Mark end, string message) - : base(start, end, message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner. - public AnchorNotFoundException(string message, Exception inner) - : base(message, inner) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + /// + /// The exception that is thrown when an alias references an anchor that does not exist. + /// + public class AnchorNotFoundException : YamlException + { + /// + /// Initializes a new instance of the class. + /// + /// The message. + public AnchorNotFoundException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + public AnchorNotFoundException(in Mark start, in Mark end, string message) + : base(start, end, message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner. + public AnchorNotFoundException(string message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/YamlDotNet/Core/CharacterAnalyzer.cs b/YamlDotNet/Core/CharacterAnalyzer.cs index 0d3ea3b3e..de95568b8 100644 --- a/YamlDotNet/Core/CharacterAnalyzer.cs +++ b/YamlDotNet/Core/CharacterAnalyzer.cs @@ -1,167 +1,167 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Diagnostics; - -namespace YamlDotNet.Core -{ - [DebuggerStepThrough] - internal sealed class CharacterAnalyzer where TBuffer : class, ILookAheadBuffer - { - public CharacterAnalyzer(TBuffer buffer) - { - this.Buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); - } - - public TBuffer Buffer { get; } - - public bool EndOfInput => Buffer.EndOfInput; - - public char Peek(int offset) - { - return Buffer.Peek(offset); - } - - public void Skip(int length) - { - Buffer.Skip(length); - } - - public bool IsAlphaNumericDashOrUnderscore(int offset = 0) - { - var character = Buffer.Peek(offset); - return - (character >= '0' && character <= '9') || - (character >= 'A' && character <= 'Z') || - (character >= 'a' && character <= 'z') || - character == '_' || - character == '-'; - } - - public bool IsAscii(int offset = 0) - { - return Buffer.Peek(offset) <= '\x7F'; - } - - public bool IsPrintable(int offset = 0) - { - var character = Buffer.Peek(offset); - return - character == '\x9' || - character == '\xA' || - character == '\xD' || - (character >= '\x20' && character <= '\x7E') || - character == '\x85' || - (character >= '\xA0' && character <= '\xD7FF') || - (character >= '\xE000' && character <= '\xFFFD'); - } - - public bool IsDigit(int offset = 0) - { - var character = Buffer.Peek(offset); - return character >= '0' && character <= '9'; - } - - public int AsDigit(int offset = 0) - { - return Buffer.Peek(offset) - '0'; - } - - public bool IsHex(int offset) - { - var character = Buffer.Peek(offset); - return - (character >= '0' && character <= '9') || - (character >= 'A' && character <= 'F') || - (character >= 'a' && character <= 'f'); - } - - public int AsHex(int offset) - { - var character = Buffer.Peek(offset); - - if (character <= '9') - { - return character - '0'; - } - if (character <= 'F') - { - return character - 'A' + 10; - } - return character - 'a' + 10; - } - - public bool IsSpace(int offset = 0) - { - return Check(' ', offset); - } - - public bool IsZero(int offset = 0) - { - return Check('\0', offset); - } - - public bool IsTab(int offset = 0) - { - return Check('\t', offset); - } - - public bool IsWhite(int offset = 0) - { - return IsSpace(offset) || IsTab(offset); - } - - public bool IsBreak(int offset = 0) - { - return Check("\r\n\x85\x2028\x2029", offset); - } - - public bool IsCrLf(int offset = 0) - { - return Check('\r', offset) && Check('\n', offset + 1); - } - - public bool IsBreakOrZero(int offset = 0) - { - return IsBreak(offset) || IsZero(offset); - } - - public bool IsWhiteBreakOrZero(int offset = 0) - { - return IsWhite(offset) || IsBreakOrZero(offset); - } - - public bool Check(char expected, int offset = 0) - { - return Buffer.Peek(offset) == expected; - } - - public bool Check(string expectedCharacters, int offset = 0) - { - // Todo: using it this way doesn't break anything, it's not really wrong... - Debug.Assert(expectedCharacters.Length > 1, "Use Check(char, int) instead."); - - var character = Buffer.Peek(offset); - return expectedCharacters.IndexOf(character) != -1; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Diagnostics; + +namespace YamlDotNet.Core +{ + [DebuggerStepThrough] + internal sealed class CharacterAnalyzer where TBuffer : class, ILookAheadBuffer + { + public CharacterAnalyzer(TBuffer buffer) + { + this.Buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + } + + public TBuffer Buffer { get; } + + public bool EndOfInput => Buffer.EndOfInput; + + public char Peek(int offset) + { + return Buffer.Peek(offset); + } + + public void Skip(int length) + { + Buffer.Skip(length); + } + + public bool IsAlphaNumericDashOrUnderscore(int offset = 0) + { + var character = Buffer.Peek(offset); + return + (character >= '0' && character <= '9') || + (character >= 'A' && character <= 'Z') || + (character >= 'a' && character <= 'z') || + character == '_' || + character == '-'; + } + + public bool IsAscii(int offset = 0) + { + return Buffer.Peek(offset) <= '\x7F'; + } + + public bool IsPrintable(int offset = 0) + { + var character = Buffer.Peek(offset); + return + character == '\x9' || + character == '\xA' || + character == '\xD' || + (character >= '\x20' && character <= '\x7E') || + character == '\x85' || + (character >= '\xA0' && character <= '\xD7FF') || + (character >= '\xE000' && character <= '\xFFFD'); + } + + public bool IsDigit(int offset = 0) + { + var character = Buffer.Peek(offset); + return character >= '0' && character <= '9'; + } + + public int AsDigit(int offset = 0) + { + return Buffer.Peek(offset) - '0'; + } + + public bool IsHex(int offset) + { + var character = Buffer.Peek(offset); + return + (character >= '0' && character <= '9') || + (character >= 'A' && character <= 'F') || + (character >= 'a' && character <= 'f'); + } + + public int AsHex(int offset) + { + var character = Buffer.Peek(offset); + + if (character <= '9') + { + return character - '0'; + } + if (character <= 'F') + { + return character - 'A' + 10; + } + return character - 'a' + 10; + } + + public bool IsSpace(int offset = 0) + { + return Check(' ', offset); + } + + public bool IsZero(int offset = 0) + { + return Check('\0', offset); + } + + public bool IsTab(int offset = 0) + { + return Check('\t', offset); + } + + public bool IsWhite(int offset = 0) + { + return IsSpace(offset) || IsTab(offset); + } + + public bool IsBreak(int offset = 0) + { + return Check("\r\n\x85\x2028\x2029", offset); + } + + public bool IsCrLf(int offset = 0) + { + return Check('\r', offset) && Check('\n', offset + 1); + } + + public bool IsBreakOrZero(int offset = 0) + { + return IsBreak(offset) || IsZero(offset); + } + + public bool IsWhiteBreakOrZero(int offset = 0) + { + return IsWhite(offset) || IsBreakOrZero(offset); + } + + public bool Check(char expected, int offset = 0) + { + return Buffer.Peek(offset) == expected; + } + + public bool Check(string expectedCharacters, int offset = 0) + { + // Todo: using it this way doesn't break anything, it's not really wrong... + Debug.Assert(expectedCharacters.Length > 1, "Use Check(char, int) instead."); + + var character = Buffer.Peek(offset); + return expectedCharacters.IndexOf(character) != -1; + } + } +} diff --git a/YamlDotNet/Core/Constants.cs b/YamlDotNet/Core/Constants.cs index 252ebadc7..7e9073271 100644 --- a/YamlDotNet/Core/Constants.cs +++ b/YamlDotNet/Core/Constants.cs @@ -1,40 +1,40 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core.Tokens; - -namespace YamlDotNet.Core -{ - /// - /// Defines constants that relate to the YAML specification. - /// - public static class Constants - { - public static readonly TagDirective[] DefaultTagDirectives = - { - new TagDirective("!", "!"), - new TagDirective("!!", "tag:yaml.org,2002:") - }; - - public const int MajorVersion = 1; - public const int MinorVersion = 3; - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core.Tokens; + +namespace YamlDotNet.Core +{ + /// + /// Defines constants that relate to the YAML specification. + /// + public static class Constants + { + public static readonly TagDirective[] DefaultTagDirectives = + { + new TagDirective("!", "!"), + new TagDirective("!!", "tag:yaml.org,2002:") + }; + + public const int MajorVersion = 1; + public const int MinorVersion = 3; + } +} diff --git a/YamlDotNet/Core/Cursor.cs b/YamlDotNet/Core/Cursor.cs index 2ffa50097..6c479e572 100644 --- a/YamlDotNet/Core/Cursor.cs +++ b/YamlDotNet/Core/Cursor.cs @@ -1,72 +1,72 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Diagnostics; - -namespace YamlDotNet.Core -{ - [DebuggerStepThrough] - public sealed class Cursor - { - public int Index { get; private set; } - public int Line { get; private set; } - public int LineOffset { get; private set; } - - public Cursor() - { - Line = 1; - } - - public Cursor(Cursor cursor) - { - Index = cursor.Index; - Line = cursor.Line; - LineOffset = cursor.LineOffset; - } - - public Mark Mark() - { - return new Mark(Index, Line, LineOffset + 1); - } - - public void Skip() - { - Index++; - LineOffset++; - } - - public void SkipLineByOffset(int offset) - { - Index += offset; - Line++; - LineOffset = 0; - } - - public void ForceSkipLineAfterNonBreak() - { - if (LineOffset != 0) - { - Line++; - LineOffset = 0; - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Diagnostics; + +namespace YamlDotNet.Core +{ + [DebuggerStepThrough] + public sealed class Cursor + { + public int Index { get; private set; } + public int Line { get; private set; } + public int LineOffset { get; private set; } + + public Cursor() + { + Line = 1; + } + + public Cursor(Cursor cursor) + { + Index = cursor.Index; + Line = cursor.Line; + LineOffset = cursor.LineOffset; + } + + public Mark Mark() + { + return new Mark(Index, Line, LineOffset + 1); + } + + public void Skip() + { + Index++; + LineOffset++; + } + + public void SkipLineByOffset(int offset) + { + Index += offset; + Line++; + LineOffset = 0; + } + + public void ForceSkipLineAfterNonBreak() + { + if (LineOffset != 0) + { + Line++; + LineOffset = 0; + } + } + } +} diff --git a/YamlDotNet/Core/Emitter.cs b/YamlDotNet/Core/Emitter.cs index 607f73b7f..9871944ca 100644 --- a/YamlDotNet/Core/Emitter.cs +++ b/YamlDotNet/Core/Emitter.cs @@ -1,1931 +1,1931 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using YamlDotNet.Core.Events; -using YamlDotNet.Helpers; -using ParsingEvent = YamlDotNet.Core.Events.ParsingEvent; -using TagDirective = YamlDotNet.Core.Tokens.TagDirective; -using VersionDirective = YamlDotNet.Core.Tokens.VersionDirective; - -namespace YamlDotNet.Core -{ - /// - /// Emits YAML streams. - /// - public class Emitter : IEmitter - { - private static readonly Regex UriReplacer = new Regex(@"[^0-9A-Za-z_\-;?@=$~\\\)\]/:&+,\.\*\(\[!]", - StandardRegexOptions.Compiled | RegexOptions.Singleline); - - private static readonly string[] newLineSeparators = new[] { "\r\n", "\r", "\n" }; - - private readonly TextWriter output; - private readonly bool outputUsesUnicodeEncoding; - - private readonly int maxSimpleKeyLength; - private readonly bool isCanonical; - private readonly bool skipAnchorName; - private readonly int bestIndent; - private readonly int bestWidth; - private EmitterState state; - - private readonly Stack states = new Stack(); - private readonly Queue events = new Queue(); - private readonly Stack indents = new Stack(); - private readonly TagDirectiveCollection tagDirectives = new TagDirectiveCollection(); - private int indent; - private int flowLevel; - private bool isMappingContext; - private bool isSimpleKeyContext; - - private int column; - private bool isWhitespace; - private bool isIndentation; - private readonly bool forceIndentLess; - - private bool isDocumentEndWritten; - - private readonly AnchorData anchorData = new AnchorData(); - private readonly TagData tagData = new TagData(); - private readonly ScalarData scalarData = new ScalarData(); - - private class AnchorData - { - public AnchorName Anchor; - public bool IsAlias; - } - - private class TagData - { - public string? Handle; - public string? Suffix; - } - - private class ScalarData - { - public string Value = string.Empty; - public bool IsMultiline; - public bool IsFlowPlainAllowed; - public bool IsBlockPlainAllowed; - public bool IsSingleQuotedAllowed; - public bool IsBlockAllowed; - public bool HasSingleQuotes; - public ScalarStyle Style; - } - - /// - /// Initializes a new instance of the class. - /// - /// The where the emitter will write. - public Emitter(TextWriter output) - : this(output, EmitterSettings.Default) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The where the emitter will write. - /// The preferred indentation. - public Emitter(TextWriter output, int bestIndent) - : this(output, bestIndent, int.MaxValue) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The where the emitter will write. - /// The preferred indentation. - /// The preferred text width. - public Emitter(TextWriter output, int bestIndent, int bestWidth) - : this(output, bestIndent, bestWidth, false) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The where the emitter will write. - /// The preferred indentation. - /// The preferred text width. - /// If true, write the output in canonical form. - public Emitter(TextWriter output, int bestIndent, int bestWidth, bool isCanonical) - : this(output, new EmitterSettings(bestIndent, bestWidth, isCanonical, 1024)) - { - } - - public Emitter(TextWriter output, EmitterSettings settings) - { - this.bestIndent = settings.BestIndent; - this.bestWidth = settings.BestWidth; - this.isCanonical = settings.IsCanonical; - this.maxSimpleKeyLength = settings.MaxSimpleKeyLength; - this.skipAnchorName = settings.SkipAnchorName; - this.forceIndentLess = !settings.IndentSequences; - - this.output = output; - this.outputUsesUnicodeEncoding = IsUnicode(output.Encoding); - } - - /// - /// Emit an evt. - /// - public void Emit(ParsingEvent @event) - { - events.Enqueue(@event); - - while (!NeedMoreEvents()) - { - var current = events.Peek(); - try - { - - AnalyzeEvent(current); - StateMachine(current); - } - finally - { - // Only dequeue after calling state_machine because it checks how many events are in the queue. - // Todo: well, move into StateMachine() then - events.Dequeue(); - } - } - } - - /// - /// Check if we need to accumulate more events before emitting. - /// - /// We accumulate extra - /// - 1 event for DOCUMENT-START - /// - 2 events for SEQUENCE-START - /// - 3 events for MAPPING-START - /// - private bool NeedMoreEvents() - { - if (events.Count == 0) - { - return true; - } - - int accumulate; - switch (events.Peek().Type) - { - case EventType.DocumentStart: - accumulate = 1; - break; - - case EventType.SequenceStart: - accumulate = 2; - break; - - case EventType.MappingStart: - accumulate = 3; - break; - - default: - return false; - } - - if (events.Count > accumulate) - { - return false; - } - - var level = 0; - foreach (var evt in events) - { - switch (evt.Type) - { - case EventType.DocumentStart: - case EventType.SequenceStart: - case EventType.MappingStart: - ++level; - break; - - case EventType.DocumentEnd: - case EventType.SequenceEnd: - case EventType.MappingEnd: - --level; - break; - } - if (level == 0) - { - return false; - } - } - - return true; - } - - private void AnalyzeEvent(ParsingEvent evt) - { - anchorData.Anchor = AnchorName.Empty; - tagData.Handle = null; - tagData.Suffix = null; - - if (evt is AnchorAlias alias) - { - AnalyzeAnchor(alias.Value, true); - return; - } - - if (evt is NodeEvent nodeEvent) - { - if (evt is Scalar scalar) - { - AnalyzeScalar(scalar); - } - - AnalyzeAnchor(nodeEvent.Anchor, false); - - if (!nodeEvent.Tag.IsEmpty && (isCanonical || nodeEvent.IsCanonical)) - { - AnalyzeTag(nodeEvent.Tag); - } - } - } - - private void AnalyzeAnchor(AnchorName anchor, bool isAlias) - { - anchorData.Anchor = anchor; - anchorData.IsAlias = isAlias; - } - - private void AnalyzeScalar(Scalar scalar) - { - var value = scalar.Value; - scalarData.Value = value; - - if (value.Length == 0) - { - if (scalar.Tag == "tag:yaml.org,2002:null") - { - scalarData.IsMultiline = false; - scalarData.IsFlowPlainAllowed = false; - scalarData.IsBlockPlainAllowed = true; - scalarData.IsSingleQuotedAllowed = false; - scalarData.IsBlockAllowed = false; - } - else - { - scalarData.IsMultiline = false; - scalarData.IsFlowPlainAllowed = false; - scalarData.IsBlockPlainAllowed = false; - scalarData.IsSingleQuotedAllowed = true; - scalarData.IsBlockAllowed = false; - } - return; - } - - var flowIndicators = false; - var blockIndicators = false; - if (value.StartsWith("---", StringComparison.Ordinal) || value.StartsWith("...", StringComparison.Ordinal)) - { - flowIndicators = true; - blockIndicators = true; - } - - var buffer = new CharacterAnalyzer(new StringLookAheadBuffer(value)); - var preceededByWhitespace = true; - var followedByWhitespace = buffer.IsWhiteBreakOrZero(1); - - var leadingSpace = false; - var leadingBreak = false; - var trailingSpace = false; - var trailingBreak = false; - var leadingQuote = false; - - var breakSpace = false; - var spaceBreak = false; - var previousSpace = false; - var previousBreak = false; - var lineOfSpaces = false; - - var lineBreaks = false; - - var specialCharacters = !ValueIsRepresentableInOutputEncoding(value); - var singleQuotes = false; - var linesOfSpaces = false; - - var isFirst = true; - while (!buffer.EndOfInput) - { - if (isFirst) - { - if (buffer.Check(@"#,[]{}&*!|>\""%@`'")) - { - flowIndicators = true; - blockIndicators = true; - leadingQuote = buffer.Check('\''); - singleQuotes |= buffer.Check('\''); - } - - if (buffer.Check("?:")) - { - flowIndicators = true; - if (followedByWhitespace) - { - blockIndicators = true; - } - } - - if (buffer.Check('-') && followedByWhitespace) - { - flowIndicators = true; - blockIndicators = true; - } - } - else - { - if (buffer.Check(",?[]{}")) - { - flowIndicators = true; - } - - if (buffer.Check(':')) - { - flowIndicators = true; - if (followedByWhitespace) - { - blockIndicators = true; - } - } - - if (buffer.Check('#') && preceededByWhitespace) - { - flowIndicators = true; - blockIndicators = true; - } - - singleQuotes |= buffer.Check('\''); - } - - if (!specialCharacters && !buffer.IsPrintable()) - { - specialCharacters = true; - } - - if (buffer.IsBreak()) - { - lineBreaks = true; - } - - if (buffer.IsSpace()) - { - if (isFirst) - { - leadingSpace = true; - } - - if (buffer.Buffer.Position >= buffer.Buffer.Length - 1) - { - trailingSpace = true; - } - - if (previousBreak) - { - breakSpace = true; - lineOfSpaces = true; - } - - previousSpace = true; - previousBreak = false; - } - else if (buffer.IsBreak()) - { - if (isFirst) - { - leadingBreak = true; - } - - if (buffer.Buffer.Position >= buffer.Buffer.Length - 1) - { - trailingBreak = true; - } - - if (previousSpace) - { - spaceBreak = true; - } - - if (lineOfSpaces) - { - linesOfSpaces = true; - } - - previousSpace = false; - previousBreak = true; - } - else - { - previousSpace = false; - previousBreak = false; - lineOfSpaces = false; - } - - preceededByWhitespace = buffer.IsWhiteBreakOrZero(); - buffer.Skip(1); - if (!buffer.EndOfInput) - { - followedByWhitespace = buffer.IsWhiteBreakOrZero(1); - } - - isFirst = false; - } - - scalarData.IsFlowPlainAllowed = true; - scalarData.IsBlockPlainAllowed = true; - scalarData.IsSingleQuotedAllowed = true; - scalarData.IsBlockAllowed = true; - - if (leadingSpace || leadingBreak || trailingSpace || trailingBreak || leadingQuote) - { - scalarData.IsFlowPlainAllowed = false; - scalarData.IsBlockPlainAllowed = false; - } - - if (trailingSpace) - { - scalarData.IsBlockAllowed = false; - } - - if (breakSpace) - { - scalarData.IsFlowPlainAllowed = false; - scalarData.IsBlockPlainAllowed = false; - scalarData.IsSingleQuotedAllowed = false; - } - - if (spaceBreak || specialCharacters) - { - scalarData.IsFlowPlainAllowed = false; - scalarData.IsBlockPlainAllowed = false; - scalarData.IsSingleQuotedAllowed = false; - } - if (linesOfSpaces) - { - scalarData.IsBlockAllowed = false; - } - - scalarData.IsMultiline = lineBreaks; - if (lineBreaks) - { - scalarData.IsFlowPlainAllowed = false; - scalarData.IsBlockPlainAllowed = false; - } - - if (flowIndicators) - { - scalarData.IsFlowPlainAllowed = false; - } - - if (blockIndicators) - { - scalarData.IsBlockPlainAllowed = false; - } - - scalarData.HasSingleQuotes = singleQuotes; - } - - private bool ValueIsRepresentableInOutputEncoding(string value) - { - if (outputUsesUnicodeEncoding) - { - return true; - } - - try - { - var encodedBytes = output.Encoding.GetBytes(value); - var decodedString = output.Encoding.GetString(encodedBytes, 0, encodedBytes.Length); - return decodedString.Equals(value); - } - catch (EncoderFallbackException) - { - return false; - } - catch (ArgumentOutOfRangeException) - { - return false; - } - } - - private bool IsUnicode(Encoding encoding) - { - return encoding is UTF8Encoding || - encoding is UnicodeEncoding || - encoding is UTF7Encoding; - } - - private void AnalyzeTag(TagName tag) - { - tagData.Handle = tag.Value; - foreach (var tagDirective in tagDirectives) - { - if (tag.Value.StartsWith(tagDirective.Prefix, StringComparison.Ordinal)) - { - tagData.Handle = tagDirective.Handle; - tagData.Suffix = tag.Value.Substring(tagDirective.Prefix.Length); - break; - } - } - } - - private void StateMachine(ParsingEvent evt) - { - if (evt is Comment comment) - { - EmitComment(comment); - return; - } - - switch (state) - { - case EmitterState.StreamStart: - EmitStreamStart(evt); - break; - - case EmitterState.FirstDocumentStart: - EmitDocumentStart(evt, true); - break; - - case EmitterState.DocumentStart: - EmitDocumentStart(evt, false); - break; - - case EmitterState.DocumentContent: - EmitDocumentContent(evt); - break; - - case EmitterState.DocumentEnd: - EmitDocumentEnd(evt); - break; - - case EmitterState.FlowSequenceFirstItem: - EmitFlowSequenceItem(evt, true); - break; - - case EmitterState.FlowSequenceItem: - EmitFlowSequenceItem(evt, false); - break; - - case EmitterState.FlowMappingFirstKey: - EmitFlowMappingKey(evt, true); - break; - - case EmitterState.FlowMappingKey: - EmitFlowMappingKey(evt, false); - break; - - case EmitterState.FlowMappingSimpleValue: - EmitFlowMappingValue(evt, true); - break; - - case EmitterState.FlowMappingValue: - EmitFlowMappingValue(evt, false); - break; - - case EmitterState.BlockSequenceFirstItem: - EmitBlockSequenceItem(evt, true); - break; - - case EmitterState.BlockSequenceItem: - EmitBlockSequenceItem(evt, false); - break; - - case EmitterState.BlockMappingFirstKey: - EmitBlockMappingKey(evt, true); - break; - - case EmitterState.BlockMappingKey: - EmitBlockMappingKey(evt, false); - break; - - case EmitterState.BlockMappingSimpleValue: - EmitBlockMappingValue(evt, true); - break; - - case EmitterState.BlockMappingValue: - EmitBlockMappingValue(evt, false); - break; - - case EmitterState.StreamEnd: - throw new YamlException("Expected nothing after STREAM-END"); - - default: - throw new InvalidOperationException(); - } - } - - private void EmitComment(Comment comment) - { - // If we're in flow mode or about to enter it: Skip comments. - if (flowLevel > 0 || state == EmitterState.FlowMappingFirstKey || state == EmitterState.FlowSequenceFirstItem) - { - return; - } - - var lines = comment.Value.Split(newLineSeparators, StringSplitOptions.None); - - if (comment.IsInline) - { - Write(" # "); - Write(string.Join(" ", lines)); - } - else - { - // If we're about to enter a YAML block we need to manually increase the indent for the comment and then decrease again. - var isFirst = state == EmitterState.BlockMappingFirstKey; - - if (isFirst) - { - IncreaseIndent(false, false); - } - - foreach (var line in lines) - { - WriteIndent(); - Write("# "); - Write(line); - WriteBreak(); - } - - if (isFirst) - { - indent = indents.Pop(); - } - } - - isIndentation = true; - } - - /// - /// Expect STREAM-START. - /// - private void EmitStreamStart(ParsingEvent evt) - { - if (!(evt is StreamStart)) - { - throw new ArgumentException("Expected STREAM-START.", nameof(evt)); - } - - indent = -1; - column = 0; - isWhitespace = true; - isIndentation = true; - - state = EmitterState.FirstDocumentStart; - } - - /// - /// Expect DOCUMENT-START or STREAM-END. - /// - private void EmitDocumentStart(ParsingEvent evt, bool isFirst) - { - if (evt is DocumentStart documentStart) - { - var isImplicit = documentStart.IsImplicit - && isFirst - && !isCanonical; - - var documentTagDirectives = NonDefaultTagsAmong(documentStart.Tags); - - if (!isFirst && !isDocumentEndWritten && (documentStart.Version != null || documentTagDirectives.Count > 0)) - { - isDocumentEndWritten = false; - WriteIndicator("...", true, false, false); - WriteIndent(); - } - - if (documentStart.Version != null) - { - AnalyzeVersionDirective(documentStart.Version); - - var documentVersion = documentStart.Version.Version; - isImplicit = false; - WriteIndicator("%YAML", true, false, false); - WriteIndicator(string.Format(CultureInfo.InvariantCulture, - "{0}.{1}", documentVersion.Major, documentVersion.Minor), - true, false, false); - WriteIndent(); - } - - foreach (var tagDirective in documentTagDirectives) - { - AppendTagDirectiveTo(tagDirective, false, tagDirectives); - } - - foreach (var tagDirective in Constants.DefaultTagDirectives) - { - AppendTagDirectiveTo(tagDirective, true, tagDirectives); - } - - if (documentTagDirectives.Count > 0) - { - isImplicit = false; - foreach (var tagDirective in Constants.DefaultTagDirectives) - { - AppendTagDirectiveTo(tagDirective, true, documentTagDirectives); - } - - foreach (var tagDirective in documentTagDirectives) - { - WriteIndicator("%TAG", true, false, false); - WriteTagHandle(tagDirective.Handle); - WriteTagContent(tagDirective.Prefix, true); - WriteIndent(); - } - } - - if (CheckEmptyDocument()) - { - isImplicit = false; - } - - if (!isImplicit) - { - WriteIndent(); - WriteIndicator("---", true, false, false); - if (isCanonical) - { - WriteIndent(); - } - } - - state = EmitterState.DocumentContent; - } - - else if (evt is StreamEnd) - { - state = EmitterState.StreamEnd; - } - else - { - throw new YamlException("Expected DOCUMENT-START or STREAM-END"); - } - } - - private TagDirectiveCollection NonDefaultTagsAmong(IEnumerable? tagCollection) - { - var directives = new TagDirectiveCollection(); - if (tagCollection == null) - { - return directives; - } - - foreach (var tagDirective in tagCollection) - { - AppendTagDirectiveTo(tagDirective, false, directives); - } - foreach (var tagDirective in Constants.DefaultTagDirectives) - { - directives.Remove(tagDirective); - } - return directives; - } - - private void AnalyzeVersionDirective(VersionDirective versionDirective) - { - if (versionDirective.Version.Major != Constants.MajorVersion || versionDirective.Version.Minor > Constants.MinorVersion) - { - throw new YamlException("Incompatible %YAML directive"); - } - } - - private static void AppendTagDirectiveTo(TagDirective value, bool allowDuplicates, TagDirectiveCollection tagDirectives) - { - if (tagDirectives.Contains(value)) - { - if (!allowDuplicates) - { - throw new YamlException("Duplicate %TAG directive."); - } - } - else - { - tagDirectives.Add(value); - } - } - - /// - /// Expect the root node. - /// - private void EmitDocumentContent(ParsingEvent evt) - { - states.Push(EmitterState.DocumentEnd); - EmitNode(evt, false, false); - } - - /// - /// Expect a node. - /// - private void EmitNode(ParsingEvent evt, bool isMapping, bool isSimpleKey) - { - isMappingContext = isMapping; - isSimpleKeyContext = isSimpleKey; - - switch (evt.Type) - { - case EventType.Alias: - EmitAlias(); - break; - - case EventType.Scalar: - EmitScalar(evt); - break; - - case EventType.SequenceStart: - EmitSequenceStart(evt); - break; - - case EventType.MappingStart: - EmitMappingStart(evt); - break; - - default: - throw new YamlException($"Expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, got {evt.Type}"); - } - } - - /// - /// Expect ALIAS. - /// - private void EmitAlias() - { - ProcessAnchor(); - state = states.Pop(); - } - - /// - /// Expect SCALAR. - /// - private void EmitScalar(ParsingEvent evt) - { - SelectScalarStyle(evt); - ProcessAnchor(); - ProcessTag(); - IncreaseIndent(true, false); - ProcessScalar(); - - indent = indents.Pop(); - state = states.Pop(); - } - - private void SelectScalarStyle(ParsingEvent evt) - { - var scalar = (Scalar)evt; - - var style = scalar.Style; - var noTag = tagData.Handle == null && tagData.Suffix == null; - - if (noTag && !scalar.IsPlainImplicit && !scalar.IsQuotedImplicit) - { - throw new YamlException("Neither tag nor isImplicit flags are specified."); - } - - if (style == ScalarStyle.Any) - { - style = scalarData.IsMultiline ? ScalarStyle.Folded : ScalarStyle.Plain; - } - - if (isCanonical) - { - style = ScalarStyle.DoubleQuoted; - } - - if (isSimpleKeyContext && scalarData.IsMultiline) - { - style = ScalarStyle.DoubleQuoted; - } - - if (style == ScalarStyle.Plain) - { - if ((flowLevel != 0 && !scalarData.IsFlowPlainAllowed) || (flowLevel == 0 && !scalarData.IsBlockPlainAllowed)) - { - style = (scalarData.IsSingleQuotedAllowed && !scalarData.HasSingleQuotes) ? ScalarStyle.SingleQuoted : ScalarStyle.DoubleQuoted; - } - if (string.IsNullOrEmpty(scalarData.Value) && (flowLevel != 0 || isSimpleKeyContext)) - { - style = ScalarStyle.SingleQuoted; - } - if (noTag && !scalar.IsPlainImplicit) - { - style = ScalarStyle.SingleQuoted; - } - } - - if (style == ScalarStyle.SingleQuoted) - { - if (!scalarData.IsSingleQuotedAllowed) - { - style = ScalarStyle.DoubleQuoted; - } - } - - if (style == ScalarStyle.Literal || style == ScalarStyle.Folded) - { - if (!scalarData.IsBlockAllowed || flowLevel != 0 || isSimpleKeyContext) - { - style = ScalarStyle.DoubleQuoted; - } - } - - scalarData.Style = style; - } - - private void ProcessScalar() - { - switch (scalarData.Style) - { - case ScalarStyle.Plain: - WritePlainScalar(scalarData.Value, !isSimpleKeyContext); - break; - - case ScalarStyle.SingleQuoted: - WriteSingleQuotedScalar(scalarData.Value, !isSimpleKeyContext); - break; - - case ScalarStyle.DoubleQuoted: - WriteDoubleQuotedScalar(scalarData.Value, !isSimpleKeyContext); - break; - - case ScalarStyle.Literal: - WriteLiteralScalar(scalarData.Value); - break; - - case ScalarStyle.Folded: - WriteFoldedScalar(scalarData.Value); - break; - - default: - throw new InvalidOperationException(); - } - } - - #region Write scalar Methods - - private void WritePlainScalar(string value, bool allowBreaks) - { - if (!isWhitespace) - { - Write(' '); - } - - var previousSpace = false; - var previousBreak = false; - for (var index = 0; index < value.Length; ++index) - { - var character = value[index]; - if (IsSpace(character)) - { - if (allowBreaks && !previousSpace && column > bestWidth && index + 1 < value.Length && value[index + 1] != ' ') - { - WriteIndent(); - } - else - { - Write(character); - } - previousSpace = true; - } - else if (IsBreak(character, out var breakCharacter)) - { - if (!previousBreak && character == '\n') - { - WriteBreak(); - } - WriteBreak(breakCharacter); - isIndentation = true; - previousBreak = true; - } - else - { - if (previousBreak) - { - WriteIndent(); - } - Write(character); - isIndentation = false; - previousSpace = false; - previousBreak = false; - } - } - - isWhitespace = false; - isIndentation = false; - } - - private void WriteSingleQuotedScalar(string value, bool allowBreaks) - { - WriteIndicator("'", true, false, false); - - var previousSpace = false; - var previousBreak = false; - - for (var index = 0; index < value.Length; ++index) - { - var character = value[index]; - if (character == ' ') - { - if (allowBreaks && !previousSpace && column > bestWidth && index != 0 && index + 1 < value.Length && - value[index + 1] != ' ') - { - WriteIndent(); - } - else - { - Write(character); - } - previousSpace = true; - } - else if (IsBreak(character, out var breakCharacter)) - { - if (!previousBreak && character == '\n') - { - WriteBreak(); - } - WriteBreak(breakCharacter); - isIndentation = true; - previousBreak = true; - } - else - { - if (previousBreak) - { - WriteIndent(); - } - if (character == '\'') - { - Write(character); - } - Write(character); - isIndentation = false; - previousSpace = false; - previousBreak = false; - } - } - - WriteIndicator("'", false, false, false); - - isWhitespace = false; - isIndentation = false; - } - - private void WriteDoubleQuotedScalar(string value, bool allowBreaks) - { - WriteIndicator("\"", true, false, false); - - var previousSpace = false; - for (var index = 0; index < value.Length; ++index) - { - var character = value[index]; - if (!IsPrintable(character) || IsBreak(character, out _) || character == '"' || character == '\\') - { - Write('\\'); - - switch (character) - { - case '\0': - Write('0'); - break; - - case '\x7': - Write('a'); - break; - - case '\x8': - Write('b'); - break; - - case '\x9': - Write('t'); - break; - - case '\xA': - Write('n'); - break; - - case '\xB': - Write('v'); - break; - - case '\xC': - Write('f'); - break; - - case '\xD': - Write('r'); - break; - - case '\x1B': - Write('e'); - break; - - case '\x22': - Write('"'); - break; - - case '\x5C': - Write('\\'); - break; - - case '\x85': - Write('N'); - break; - - case '\xA0': - Write('_'); - break; - - case '\x2028': - Write('L'); - break; - - case '\x2029': - Write('P'); - break; - - default: - var code = (ushort)character; - if (code <= 0xFF) - { - Write('x'); - Write(code.ToString("X02", CultureInfo.InvariantCulture)); - } - else if (IsHighSurrogate(character)) - { - if (index + 1 < value.Length && IsLowSurrogate(value[index + 1])) - { - Write('U'); - Write(char.ConvertToUtf32(character, value[index + 1]).ToString("X08", CultureInfo.InvariantCulture)); - index++; - } - else - { - throw new SyntaxErrorException("While writing a quoted scalar, found an orphaned high surrogate."); - } - } - else - { - Write('u'); - Write(code.ToString("X04", CultureInfo.InvariantCulture)); - } - break; - } - previousSpace = false; - } - else if (character == ' ') - { - if (allowBreaks && !previousSpace && column > bestWidth && index > 0 && index + 1 < value.Length) - { - WriteIndent(); - if (value[index + 1] == ' ') - { - Write('\\'); - } - } - else - { - Write(character); - } - previousSpace = true; - } - else - { - Write(character); - previousSpace = false; - } - } - - WriteIndicator("\"", false, false, false); - - isWhitespace = false; - isIndentation = false; - } - - private void WriteLiteralScalar(string value) - { - var previousBreak = true; - - WriteIndicator("|", true, false, false); - WriteBlockScalarHints(value); - WriteBreak(); - - isIndentation = true; - isWhitespace = true; - - for (var i = 0; i < value.Length; ++i) - { - var character = value[i]; - if (character == '\r' && (i + 1) < value.Length && value[i + 1] == '\n') - { - continue; - } - - if (IsBreak(character, out var breakCharacter)) - { - WriteBreak(breakCharacter); - isIndentation = true; - previousBreak = true; - } - else - { - if (previousBreak) - { - WriteIndent(); - } - Write(character); - isIndentation = false; - previousBreak = false; - } - } - } - - private void WriteFoldedScalar(string value) - { - var previousBreak = true; - var leadingSpaces = true; - - WriteIndicator(">", true, false, false); - WriteBlockScalarHints(value); - WriteBreak(); - - isIndentation = true; - isWhitespace = true; - - for (var i = 0; i < value.Length; ++i) - { - var character = value[i]; - if (IsBreak(character, out var breakCharacter)) - { - if (character == '\r' && (i + 1) < value.Length && value[i + 1] == '\n') - { - continue; - } - - if (!previousBreak && !leadingSpaces && breakCharacter == '\n') - { - var k = 0; - while (i + k < value.Length && IsBreak(value[i + k], out _)) - { - ++k; - } - if (i + k < value.Length && !(IsBlank(value[i + k]) || IsBreak(value[i + k], out _))) - { - WriteBreak(); - } - } - - WriteBreak(breakCharacter); - isIndentation = true; - previousBreak = true; - } - else - { - if (previousBreak) - { - WriteIndent(); - leadingSpaces = IsBlank(character); - } - if (!previousBreak && character == ' ' && i + 1 < value.Length && value[i + 1] != ' ' && column > bestWidth) - { - WriteIndent(); - } - else - { - Write(character); - } - isIndentation = false; - previousBreak = false; - } - } - } - - // Todo: isn't this what CharacterAnalyser is for? - private static bool IsSpace(char character) - { - return character == ' '; - } - - private static bool IsBreak(char character, out char breakChar) - { - switch (character) - { - case '\r': - case '\n': - case '\x85': - breakChar = '\n'; - return true; - - case '\x2028': - case '\x2029': - breakChar = character; - return true; - - default: - breakChar = '\0'; - return false; - } - } - - private static bool IsBlank(char character) - { - return character == ' ' || character == '\t'; - } - - private static bool IsPrintable(char character) - { - return - character == '\x9' || - character == '\xA' || - character == '\xD' || - (character >= '\x20' && character <= '\x7E') || - character == '\x85' || - (character >= '\xA0' && character <= '\xD7FF') || - (character >= '\xE000' && character <= '\xFFFD'); - } - - private static bool IsHighSurrogate(char c) - { - return 0xD800 <= c && c <= 0xDBFF; - } - - private static bool IsLowSurrogate(char c) - { - return 0xDC00 <= c && c <= 0xDFFF; - } - - #endregion - - /// - /// Expect SEQUENCE-START. - /// - private void EmitSequenceStart(ParsingEvent evt) - { - ProcessAnchor(); - ProcessTag(); - - var sequenceStart = (SequenceStart)evt; - - if (flowLevel != 0 || isCanonical || sequenceStart.Style == SequenceStyle.Flow || CheckEmptySequence()) - { - state = EmitterState.FlowSequenceFirstItem; - } - else - { - state = EmitterState.BlockSequenceFirstItem; - } - } - - /// - /// Expect MAPPING-START. - /// - private void EmitMappingStart(ParsingEvent evt) - { - ProcessAnchor(); - ProcessTag(); - - var mappingStart = (MappingStart)evt; - - if (flowLevel != 0 || isCanonical || mappingStart.Style == MappingStyle.Flow || CheckEmptyMapping()) - { - state = EmitterState.FlowMappingFirstKey; - } - else - { - state = EmitterState.BlockMappingFirstKey; - } - } - - private void ProcessAnchor() - { - if (!anchorData.Anchor.IsEmpty && !skipAnchorName) - { - WriteIndicator(anchorData.IsAlias ? "*" : "&", true, false, false); - WriteAnchor(anchorData.Anchor); - } - } - - private void ProcessTag() - { - if (tagData.Handle == null && tagData.Suffix == null) - { - return; - } - - if (tagData.Handle != null) - { - WriteTagHandle(tagData.Handle); - if (tagData.Suffix != null) - { - WriteTagContent(tagData.Suffix, false); - } - } - else - { - WriteIndicator("!<", true, false, false); - WriteTagContent(tagData.Suffix!, false); - WriteIndicator(">", false, false, false); - } - } - - /// - /// Expect DOCUMENT-END. - /// - private void EmitDocumentEnd(ParsingEvent evt) - { - if (evt is DocumentEnd documentEnd) - { - WriteIndent(); - if (!documentEnd.IsImplicit) - { - WriteIndicator("...", true, false, false); - WriteIndent(); - isDocumentEndWritten = true; - } - - state = EmitterState.DocumentStart; - - tagDirectives.Clear(); - } - else - { - throw new YamlException("Expected DOCUMENT-END."); - } - } - - /// - /// Expect a flow item node. - /// - private void EmitFlowSequenceItem(ParsingEvent evt, bool isFirst) - { - if (isFirst) - { - WriteIndicator("[", true, true, false); - IncreaseIndent(true, false); - ++flowLevel; - } - - if (evt is SequenceEnd) - { - --flowLevel; - indent = indents.Pop(); - if (isCanonical && !isFirst) - { - WriteIndicator(",", false, false, false); - WriteIndent(); - } - WriteIndicator("]", false, false, false); - state = states.Pop(); - return; - } - - if (!isFirst) - { - WriteIndicator(",", false, false, false); - } - - if (isCanonical || column > bestWidth) - { - WriteIndent(); - } - - states.Push(EmitterState.FlowSequenceItem); - - EmitNode(evt, false, false); - } - - /// - /// Expect a flow key node. - /// - private void EmitFlowMappingKey(ParsingEvent evt, bool isFirst) - { - if (isFirst) - { - WriteIndicator("{", true, true, false); - IncreaseIndent(true, false); - ++flowLevel; - } - - if (evt is MappingEnd) - { - --flowLevel; - indent = indents.Pop(); - if (isCanonical && !isFirst) - { - WriteIndicator(",", false, false, false); - WriteIndent(); - } - WriteIndicator("}", false, false, false); - state = states.Pop(); - return; - } - - if (!isFirst) - { - WriteIndicator(",", false, false, false); - } - if (isCanonical || column > bestWidth) - { - WriteIndent(); - } - - if (!isCanonical && CheckSimpleKey()) - { - states.Push(EmitterState.FlowMappingSimpleValue); - EmitNode(evt, true, true); - } - else - { - WriteIndicator("?", true, false, false); - states.Push(EmitterState.FlowMappingValue); - EmitNode(evt, true, false); - } - } - - /// - /// Expect a flow value node. - /// - private void EmitFlowMappingValue(ParsingEvent evt, bool isSimple) - { - if (isSimple) - { - WriteIndicator(":", false, false, false); - } - else - { - if (isCanonical || column > bestWidth) - { - WriteIndent(); - } - WriteIndicator(":", true, false, false); - } - states.Push(EmitterState.FlowMappingKey); - EmitNode(evt, true, false); - } - - /// - /// Expect a block item node. - /// - private void EmitBlockSequenceItem(ParsingEvent evt, bool isFirst) - { - if (isFirst) - { - IncreaseIndent(false, (isMappingContext && !isIndentation)); - } - - if (evt is SequenceEnd) - { - indent = indents.Pop(); - state = states.Pop(); - return; - } - - WriteIndent(); - WriteIndicator("-", true, false, true); - states.Push(EmitterState.BlockSequenceItem); - - EmitNode(evt, false, false); - } - - /// - /// Expect a block key node. - /// - private void EmitBlockMappingKey(ParsingEvent evt, bool isFirst) - { - if (isFirst) - { - IncreaseIndent(false, false); - } - - if (evt is MappingEnd) - { - indent = indents.Pop(); - state = states.Pop(); - return; - } - - WriteIndent(); - - if (CheckSimpleKey()) - { - states.Push(EmitterState.BlockMappingSimpleValue); - EmitNode(evt, true, true); - } - else - { - WriteIndicator("?", true, false, true); - states.Push(EmitterState.BlockMappingValue); - EmitNode(evt, true, false); - } - } - - /// - /// Expect a block value node. - /// - private void EmitBlockMappingValue(ParsingEvent evt, bool isSimple) - { - if (isSimple) - { - WriteIndicator(":", false, false, false); - } - else - { - WriteIndent(); - WriteIndicator(":", true, false, true); - } - states.Push(EmitterState.BlockMappingKey); - EmitNode(evt, true, false); - } - - private void IncreaseIndent(bool isFlow, bool isIndentless) - { - indents.Push(indent); - - if (indent < 0) - { - indent = isFlow ? bestIndent : 0; - } - else if (!isIndentless || !forceIndentLess) - { - indent += bestIndent; - } - } - - #region Check Methods - - /// - /// Check if the document content is an empty scalar. - /// - private bool CheckEmptyDocument() - { - var index = 0; - foreach (var parsingEvent in events) - { - index++; - if (index == 2) - { - if (parsingEvent is Scalar scalar) - { - return string.IsNullOrEmpty(scalar.Value); - } - break; - } - } - - return false; - } - - /// - /// Check if the next node can be expressed as a simple key. - /// - private bool CheckSimpleKey() - { - if (events.Count < 1) - { - return false; - } - - int length; - switch (events.Peek().Type) - { - case EventType.Alias: - length = AnchorNameLength(anchorData.Anchor); - break; - - case EventType.Scalar: - if (scalarData.IsMultiline) - { - return false; - } - - length = - AnchorNameLength(anchorData.Anchor) + - SafeStringLength(tagData.Handle) + - SafeStringLength(tagData.Suffix) + - SafeStringLength(scalarData.Value); - break; - - case EventType.SequenceStart: - if (!CheckEmptySequence()) - { - return false; - } - length = - AnchorNameLength(anchorData.Anchor) + - SafeStringLength(tagData.Handle) + - SafeStringLength(tagData.Suffix); - break; - - case EventType.MappingStart: - if (!CheckEmptySequence()) - { - return false; - } - length = - AnchorNameLength(anchorData.Anchor) + - SafeStringLength(tagData.Handle) + - SafeStringLength(tagData.Suffix); - break; - - default: - return false; - } - - return length <= maxSimpleKeyLength; - } - - private int AnchorNameLength(AnchorName value) - { - return value.IsEmpty ? 0 : value.Value.Length; - } - - private int SafeStringLength(string? value) - { - return value == null ? 0 : value.Length; - } - - private bool CheckEmptySequence() => CheckEmptyStructure(); - private bool CheckEmptyMapping() => CheckEmptyStructure(); - - private bool CheckEmptyStructure() - where TStart : NodeEvent - where TEnd : ParsingEvent - { - if (events.Count < 2) - { - return false; - } - - using var enumerator = events.GetEnumerator(); - return enumerator.MoveNext() - && enumerator.Current is TStart - && enumerator.MoveNext() - && enumerator.Current is TEnd; - } - #endregion - - #region Write Methods - - private void WriteBlockScalarHints(string value) - { - var analyzer = new CharacterAnalyzer(new StringLookAheadBuffer(value)); - - if (analyzer.IsSpace() || analyzer.IsBreak()) - { - var indentHint = bestIndent.ToString(CultureInfo.InvariantCulture); - WriteIndicator(indentHint, false, false, false); - } - - string? chompHint = null; - if (value.Length == 0 || !analyzer.IsBreak(value.Length - 1)) - { - chompHint = "-"; - } - else if (value.Length >= 2 && analyzer.IsBreak(value.Length - 2)) - { - chompHint = "+"; - } - - if (chompHint != null) - { - WriteIndicator(chompHint, false, false, false); - } - } - - private void WriteIndicator(string indicator, bool needWhitespace, bool whitespace, bool indentation) - { - if (needWhitespace && !isWhitespace) - { - Write(' '); - } - - Write(indicator); - - isWhitespace = whitespace; - isIndentation &= indentation; - } - - private void WriteIndent() - { - var currentIndent = Math.Max(indent, 0); - - var isBreakRequired = !isIndentation - || column > currentIndent - || (column == currentIndent && !isWhitespace); - - if (isBreakRequired) - { - WriteBreak(); - } - - while (column < currentIndent) - { - Write(' '); - } - - isWhitespace = true; - isIndentation = true; - } - - private void WriteAnchor(AnchorName value) - { - Write(value.Value); - - isWhitespace = false; - isIndentation = false; - } - - private void WriteTagHandle(string value) - { - if (!isWhitespace) - { - Write(' '); - } - - Write(value); - - isWhitespace = false; - isIndentation = false; - } - - private void WriteTagContent(string value, bool needsWhitespace) - { - if (needsWhitespace && !isWhitespace) - { - Write(' '); - } - - Write(UrlEncode(value)); - - isWhitespace = false; - isIndentation = false; - } - - private static string UrlEncode(string text) - { - return UriReplacer.Replace(text, delegate (Match match) - { - using var bufferBuilder = StringBuilderPool.Rent(); - var buffer = bufferBuilder.Builder; - foreach (var toEncode in Encoding.UTF8.GetBytes(match.Value)) - { - buffer.AppendFormat("%{0:X02}", toEncode); - } - return buffer.ToString(); - }); - } - - private void Write(char value) - { - output.Write(value); - ++column; - } - - private void Write(string value) - { - output.Write(value); - column += value.Length; - } - - private void WriteBreak(char breakCharacter = '\n') - { - if (breakCharacter == '\n') - { - output.WriteLine(); - } - else - { - output.Write(breakCharacter); - } - column = 0; - } - - #endregion - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using YamlDotNet.Core.Events; +using YamlDotNet.Helpers; +using ParsingEvent = YamlDotNet.Core.Events.ParsingEvent; +using TagDirective = YamlDotNet.Core.Tokens.TagDirective; +using VersionDirective = YamlDotNet.Core.Tokens.VersionDirective; + +namespace YamlDotNet.Core +{ + /// + /// Emits YAML streams. + /// + public class Emitter : IEmitter + { + private static readonly Regex UriReplacer = new Regex(@"[^0-9A-Za-z_\-;?@=$~\\\)\]/:&+,\.\*\(\[!]", + StandardRegexOptions.Compiled | RegexOptions.Singleline); + + private static readonly string[] newLineSeparators = new[] { "\r\n", "\r", "\n" }; + + private readonly TextWriter output; + private readonly bool outputUsesUnicodeEncoding; + + private readonly int maxSimpleKeyLength; + private readonly bool isCanonical; + private readonly bool skipAnchorName; + private readonly int bestIndent; + private readonly int bestWidth; + private EmitterState state; + + private readonly Stack states = new Stack(); + private readonly Queue events = new Queue(); + private readonly Stack indents = new Stack(); + private readonly TagDirectiveCollection tagDirectives = new TagDirectiveCollection(); + private int indent; + private int flowLevel; + private bool isMappingContext; + private bool isSimpleKeyContext; + + private int column; + private bool isWhitespace; + private bool isIndentation; + private readonly bool forceIndentLess; + + private bool isDocumentEndWritten; + + private readonly AnchorData anchorData = new AnchorData(); + private readonly TagData tagData = new TagData(); + private readonly ScalarData scalarData = new ScalarData(); + + private class AnchorData + { + public AnchorName Anchor; + public bool IsAlias; + } + + private class TagData + { + public string? Handle; + public string? Suffix; + } + + private class ScalarData + { + public string Value = string.Empty; + public bool IsMultiline; + public bool IsFlowPlainAllowed; + public bool IsBlockPlainAllowed; + public bool IsSingleQuotedAllowed; + public bool IsBlockAllowed; + public bool HasSingleQuotes; + public ScalarStyle Style; + } + + /// + /// Initializes a new instance of the class. + /// + /// The where the emitter will write. + public Emitter(TextWriter output) + : this(output, EmitterSettings.Default) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The where the emitter will write. + /// The preferred indentation. + public Emitter(TextWriter output, int bestIndent) + : this(output, bestIndent, int.MaxValue) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The where the emitter will write. + /// The preferred indentation. + /// The preferred text width. + public Emitter(TextWriter output, int bestIndent, int bestWidth) + : this(output, bestIndent, bestWidth, false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The where the emitter will write. + /// The preferred indentation. + /// The preferred text width. + /// If true, write the output in canonical form. + public Emitter(TextWriter output, int bestIndent, int bestWidth, bool isCanonical) + : this(output, new EmitterSettings(bestIndent, bestWidth, isCanonical, 1024)) + { + } + + public Emitter(TextWriter output, EmitterSettings settings) + { + this.bestIndent = settings.BestIndent; + this.bestWidth = settings.BestWidth; + this.isCanonical = settings.IsCanonical; + this.maxSimpleKeyLength = settings.MaxSimpleKeyLength; + this.skipAnchorName = settings.SkipAnchorName; + this.forceIndentLess = !settings.IndentSequences; + + this.output = output; + this.outputUsesUnicodeEncoding = IsUnicode(output.Encoding); + } + + /// + /// Emit an evt. + /// + public void Emit(ParsingEvent @event) + { + events.Enqueue(@event); + + while (!NeedMoreEvents()) + { + var current = events.Peek(); + try + { + + AnalyzeEvent(current); + StateMachine(current); + } + finally + { + // Only dequeue after calling state_machine because it checks how many events are in the queue. + // Todo: well, move into StateMachine() then + events.Dequeue(); + } + } + } + + /// + /// Check if we need to accumulate more events before emitting. + /// + /// We accumulate extra + /// - 1 event for DOCUMENT-START + /// - 2 events for SEQUENCE-START + /// - 3 events for MAPPING-START + /// + private bool NeedMoreEvents() + { + if (events.Count == 0) + { + return true; + } + + int accumulate; + switch (events.Peek().Type) + { + case EventType.DocumentStart: + accumulate = 1; + break; + + case EventType.SequenceStart: + accumulate = 2; + break; + + case EventType.MappingStart: + accumulate = 3; + break; + + default: + return false; + } + + if (events.Count > accumulate) + { + return false; + } + + var level = 0; + foreach (var evt in events) + { + switch (evt.Type) + { + case EventType.DocumentStart: + case EventType.SequenceStart: + case EventType.MappingStart: + ++level; + break; + + case EventType.DocumentEnd: + case EventType.SequenceEnd: + case EventType.MappingEnd: + --level; + break; + } + if (level == 0) + { + return false; + } + } + + return true; + } + + private void AnalyzeEvent(ParsingEvent evt) + { + anchorData.Anchor = AnchorName.Empty; + tagData.Handle = null; + tagData.Suffix = null; + + if (evt is AnchorAlias alias) + { + AnalyzeAnchor(alias.Value, true); + return; + } + + if (evt is NodeEvent nodeEvent) + { + if (evt is Scalar scalar) + { + AnalyzeScalar(scalar); + } + + AnalyzeAnchor(nodeEvent.Anchor, false); + + if (!nodeEvent.Tag.IsEmpty && (isCanonical || nodeEvent.IsCanonical)) + { + AnalyzeTag(nodeEvent.Tag); + } + } + } + + private void AnalyzeAnchor(AnchorName anchor, bool isAlias) + { + anchorData.Anchor = anchor; + anchorData.IsAlias = isAlias; + } + + private void AnalyzeScalar(Scalar scalar) + { + var value = scalar.Value; + scalarData.Value = value; + + if (value.Length == 0) + { + if (scalar.Tag == "tag:yaml.org,2002:null") + { + scalarData.IsMultiline = false; + scalarData.IsFlowPlainAllowed = false; + scalarData.IsBlockPlainAllowed = true; + scalarData.IsSingleQuotedAllowed = false; + scalarData.IsBlockAllowed = false; + } + else + { + scalarData.IsMultiline = false; + scalarData.IsFlowPlainAllowed = false; + scalarData.IsBlockPlainAllowed = false; + scalarData.IsSingleQuotedAllowed = true; + scalarData.IsBlockAllowed = false; + } + return; + } + + var flowIndicators = false; + var blockIndicators = false; + if (value.StartsWith("---", StringComparison.Ordinal) || value.StartsWith("...", StringComparison.Ordinal)) + { + flowIndicators = true; + blockIndicators = true; + } + + var buffer = new CharacterAnalyzer(new StringLookAheadBuffer(value)); + var preceededByWhitespace = true; + var followedByWhitespace = buffer.IsWhiteBreakOrZero(1); + + var leadingSpace = false; + var leadingBreak = false; + var trailingSpace = false; + var trailingBreak = false; + var leadingQuote = false; + + var breakSpace = false; + var spaceBreak = false; + var previousSpace = false; + var previousBreak = false; + var lineOfSpaces = false; + + var lineBreaks = false; + + var specialCharacters = !ValueIsRepresentableInOutputEncoding(value); + var singleQuotes = false; + var linesOfSpaces = false; + + var isFirst = true; + while (!buffer.EndOfInput) + { + if (isFirst) + { + if (buffer.Check(@"#,[]{}&*!|>\""%@`'")) + { + flowIndicators = true; + blockIndicators = true; + leadingQuote = buffer.Check('\''); + singleQuotes |= buffer.Check('\''); + } + + if (buffer.Check("?:")) + { + flowIndicators = true; + if (followedByWhitespace) + { + blockIndicators = true; + } + } + + if (buffer.Check('-') && followedByWhitespace) + { + flowIndicators = true; + blockIndicators = true; + } + } + else + { + if (buffer.Check(",?[]{}")) + { + flowIndicators = true; + } + + if (buffer.Check(':')) + { + flowIndicators = true; + if (followedByWhitespace) + { + blockIndicators = true; + } + } + + if (buffer.Check('#') && preceededByWhitespace) + { + flowIndicators = true; + blockIndicators = true; + } + + singleQuotes |= buffer.Check('\''); + } + + if (!specialCharacters && !buffer.IsPrintable()) + { + specialCharacters = true; + } + + if (buffer.IsBreak()) + { + lineBreaks = true; + } + + if (buffer.IsSpace()) + { + if (isFirst) + { + leadingSpace = true; + } + + if (buffer.Buffer.Position >= buffer.Buffer.Length - 1) + { + trailingSpace = true; + } + + if (previousBreak) + { + breakSpace = true; + lineOfSpaces = true; + } + + previousSpace = true; + previousBreak = false; + } + else if (buffer.IsBreak()) + { + if (isFirst) + { + leadingBreak = true; + } + + if (buffer.Buffer.Position >= buffer.Buffer.Length - 1) + { + trailingBreak = true; + } + + if (previousSpace) + { + spaceBreak = true; + } + + if (lineOfSpaces) + { + linesOfSpaces = true; + } + + previousSpace = false; + previousBreak = true; + } + else + { + previousSpace = false; + previousBreak = false; + lineOfSpaces = false; + } + + preceededByWhitespace = buffer.IsWhiteBreakOrZero(); + buffer.Skip(1); + if (!buffer.EndOfInput) + { + followedByWhitespace = buffer.IsWhiteBreakOrZero(1); + } + + isFirst = false; + } + + scalarData.IsFlowPlainAllowed = true; + scalarData.IsBlockPlainAllowed = true; + scalarData.IsSingleQuotedAllowed = true; + scalarData.IsBlockAllowed = true; + + if (leadingSpace || leadingBreak || trailingSpace || trailingBreak || leadingQuote) + { + scalarData.IsFlowPlainAllowed = false; + scalarData.IsBlockPlainAllowed = false; + } + + if (trailingSpace) + { + scalarData.IsBlockAllowed = false; + } + + if (breakSpace) + { + scalarData.IsFlowPlainAllowed = false; + scalarData.IsBlockPlainAllowed = false; + scalarData.IsSingleQuotedAllowed = false; + } + + if (spaceBreak || specialCharacters) + { + scalarData.IsFlowPlainAllowed = false; + scalarData.IsBlockPlainAllowed = false; + scalarData.IsSingleQuotedAllowed = false; + } + if (linesOfSpaces) + { + scalarData.IsBlockAllowed = false; + } + + scalarData.IsMultiline = lineBreaks; + if (lineBreaks) + { + scalarData.IsFlowPlainAllowed = false; + scalarData.IsBlockPlainAllowed = false; + } + + if (flowIndicators) + { + scalarData.IsFlowPlainAllowed = false; + } + + if (blockIndicators) + { + scalarData.IsBlockPlainAllowed = false; + } + + scalarData.HasSingleQuotes = singleQuotes; + } + + private bool ValueIsRepresentableInOutputEncoding(string value) + { + if (outputUsesUnicodeEncoding) + { + return true; + } + + try + { + var encodedBytes = output.Encoding.GetBytes(value); + var decodedString = output.Encoding.GetString(encodedBytes, 0, encodedBytes.Length); + return decodedString.Equals(value); + } + catch (EncoderFallbackException) + { + return false; + } + catch (ArgumentOutOfRangeException) + { + return false; + } + } + + private bool IsUnicode(Encoding encoding) + { + return encoding is UTF8Encoding || + encoding is UnicodeEncoding || + encoding is UTF7Encoding; + } + + private void AnalyzeTag(TagName tag) + { + tagData.Handle = tag.Value; + foreach (var tagDirective in tagDirectives) + { + if (tag.Value.StartsWith(tagDirective.Prefix, StringComparison.Ordinal)) + { + tagData.Handle = tagDirective.Handle; + tagData.Suffix = tag.Value.Substring(tagDirective.Prefix.Length); + break; + } + } + } + + private void StateMachine(ParsingEvent evt) + { + if (evt is Comment comment) + { + EmitComment(comment); + return; + } + + switch (state) + { + case EmitterState.StreamStart: + EmitStreamStart(evt); + break; + + case EmitterState.FirstDocumentStart: + EmitDocumentStart(evt, true); + break; + + case EmitterState.DocumentStart: + EmitDocumentStart(evt, false); + break; + + case EmitterState.DocumentContent: + EmitDocumentContent(evt); + break; + + case EmitterState.DocumentEnd: + EmitDocumentEnd(evt); + break; + + case EmitterState.FlowSequenceFirstItem: + EmitFlowSequenceItem(evt, true); + break; + + case EmitterState.FlowSequenceItem: + EmitFlowSequenceItem(evt, false); + break; + + case EmitterState.FlowMappingFirstKey: + EmitFlowMappingKey(evt, true); + break; + + case EmitterState.FlowMappingKey: + EmitFlowMappingKey(evt, false); + break; + + case EmitterState.FlowMappingSimpleValue: + EmitFlowMappingValue(evt, true); + break; + + case EmitterState.FlowMappingValue: + EmitFlowMappingValue(evt, false); + break; + + case EmitterState.BlockSequenceFirstItem: + EmitBlockSequenceItem(evt, true); + break; + + case EmitterState.BlockSequenceItem: + EmitBlockSequenceItem(evt, false); + break; + + case EmitterState.BlockMappingFirstKey: + EmitBlockMappingKey(evt, true); + break; + + case EmitterState.BlockMappingKey: + EmitBlockMappingKey(evt, false); + break; + + case EmitterState.BlockMappingSimpleValue: + EmitBlockMappingValue(evt, true); + break; + + case EmitterState.BlockMappingValue: + EmitBlockMappingValue(evt, false); + break; + + case EmitterState.StreamEnd: + throw new YamlException("Expected nothing after STREAM-END"); + + default: + throw new InvalidOperationException(); + } + } + + private void EmitComment(Comment comment) + { + // If we're in flow mode or about to enter it: Skip comments. + if (flowLevel > 0 || state == EmitterState.FlowMappingFirstKey || state == EmitterState.FlowSequenceFirstItem) + { + return; + } + + var lines = comment.Value.Split(newLineSeparators, StringSplitOptions.None); + + if (comment.IsInline) + { + Write(" # "); + Write(string.Join(" ", lines)); + } + else + { + // If we're about to enter a YAML block we need to manually increase the indent for the comment and then decrease again. + var isFirst = state == EmitterState.BlockMappingFirstKey; + + if (isFirst) + { + IncreaseIndent(false, false); + } + + foreach (var line in lines) + { + WriteIndent(); + Write("# "); + Write(line); + WriteBreak(); + } + + if (isFirst) + { + indent = indents.Pop(); + } + } + + isIndentation = true; + } + + /// + /// Expect STREAM-START. + /// + private void EmitStreamStart(ParsingEvent evt) + { + if (!(evt is StreamStart)) + { + throw new ArgumentException("Expected STREAM-START.", nameof(evt)); + } + + indent = -1; + column = 0; + isWhitespace = true; + isIndentation = true; + + state = EmitterState.FirstDocumentStart; + } + + /// + /// Expect DOCUMENT-START or STREAM-END. + /// + private void EmitDocumentStart(ParsingEvent evt, bool isFirst) + { + if (evt is DocumentStart documentStart) + { + var isImplicit = documentStart.IsImplicit + && isFirst + && !isCanonical; + + var documentTagDirectives = NonDefaultTagsAmong(documentStart.Tags); + + if (!isFirst && !isDocumentEndWritten && (documentStart.Version != null || documentTagDirectives.Count > 0)) + { + isDocumentEndWritten = false; + WriteIndicator("...", true, false, false); + WriteIndent(); + } + + if (documentStart.Version != null) + { + AnalyzeVersionDirective(documentStart.Version); + + var documentVersion = documentStart.Version.Version; + isImplicit = false; + WriteIndicator("%YAML", true, false, false); + WriteIndicator(string.Format(CultureInfo.InvariantCulture, + "{0}.{1}", documentVersion.Major, documentVersion.Minor), + true, false, false); + WriteIndent(); + } + + foreach (var tagDirective in documentTagDirectives) + { + AppendTagDirectiveTo(tagDirective, false, tagDirectives); + } + + foreach (var tagDirective in Constants.DefaultTagDirectives) + { + AppendTagDirectiveTo(tagDirective, true, tagDirectives); + } + + if (documentTagDirectives.Count > 0) + { + isImplicit = false; + foreach (var tagDirective in Constants.DefaultTagDirectives) + { + AppendTagDirectiveTo(tagDirective, true, documentTagDirectives); + } + + foreach (var tagDirective in documentTagDirectives) + { + WriteIndicator("%TAG", true, false, false); + WriteTagHandle(tagDirective.Handle); + WriteTagContent(tagDirective.Prefix, true); + WriteIndent(); + } + } + + if (CheckEmptyDocument()) + { + isImplicit = false; + } + + if (!isImplicit) + { + WriteIndent(); + WriteIndicator("---", true, false, false); + if (isCanonical) + { + WriteIndent(); + } + } + + state = EmitterState.DocumentContent; + } + + else if (evt is StreamEnd) + { + state = EmitterState.StreamEnd; + } + else + { + throw new YamlException("Expected DOCUMENT-START or STREAM-END"); + } + } + + private TagDirectiveCollection NonDefaultTagsAmong(IEnumerable? tagCollection) + { + var directives = new TagDirectiveCollection(); + if (tagCollection == null) + { + return directives; + } + + foreach (var tagDirective in tagCollection) + { + AppendTagDirectiveTo(tagDirective, false, directives); + } + foreach (var tagDirective in Constants.DefaultTagDirectives) + { + directives.Remove(tagDirective); + } + return directives; + } + + private void AnalyzeVersionDirective(VersionDirective versionDirective) + { + if (versionDirective.Version.Major != Constants.MajorVersion || versionDirective.Version.Minor > Constants.MinorVersion) + { + throw new YamlException("Incompatible %YAML directive"); + } + } + + private static void AppendTagDirectiveTo(TagDirective value, bool allowDuplicates, TagDirectiveCollection tagDirectives) + { + if (tagDirectives.Contains(value)) + { + if (!allowDuplicates) + { + throw new YamlException("Duplicate %TAG directive."); + } + } + else + { + tagDirectives.Add(value); + } + } + + /// + /// Expect the root node. + /// + private void EmitDocumentContent(ParsingEvent evt) + { + states.Push(EmitterState.DocumentEnd); + EmitNode(evt, false, false); + } + + /// + /// Expect a node. + /// + private void EmitNode(ParsingEvent evt, bool isMapping, bool isSimpleKey) + { + isMappingContext = isMapping; + isSimpleKeyContext = isSimpleKey; + + switch (evt.Type) + { + case EventType.Alias: + EmitAlias(); + break; + + case EventType.Scalar: + EmitScalar(evt); + break; + + case EventType.SequenceStart: + EmitSequenceStart(evt); + break; + + case EventType.MappingStart: + EmitMappingStart(evt); + break; + + default: + throw new YamlException($"Expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, got {evt.Type}"); + } + } + + /// + /// Expect ALIAS. + /// + private void EmitAlias() + { + ProcessAnchor(); + state = states.Pop(); + } + + /// + /// Expect SCALAR. + /// + private void EmitScalar(ParsingEvent evt) + { + SelectScalarStyle(evt); + ProcessAnchor(); + ProcessTag(); + IncreaseIndent(true, false); + ProcessScalar(); + + indent = indents.Pop(); + state = states.Pop(); + } + + private void SelectScalarStyle(ParsingEvent evt) + { + var scalar = (Scalar)evt; + + var style = scalar.Style; + var noTag = tagData.Handle == null && tagData.Suffix == null; + + if (noTag && !scalar.IsPlainImplicit && !scalar.IsQuotedImplicit) + { + throw new YamlException("Neither tag nor isImplicit flags are specified."); + } + + if (style == ScalarStyle.Any) + { + style = scalarData.IsMultiline ? ScalarStyle.Folded : ScalarStyle.Plain; + } + + if (isCanonical) + { + style = ScalarStyle.DoubleQuoted; + } + + if (isSimpleKeyContext && scalarData.IsMultiline) + { + style = ScalarStyle.DoubleQuoted; + } + + if (style == ScalarStyle.Plain) + { + if ((flowLevel != 0 && !scalarData.IsFlowPlainAllowed) || (flowLevel == 0 && !scalarData.IsBlockPlainAllowed)) + { + style = (scalarData.IsSingleQuotedAllowed && !scalarData.HasSingleQuotes) ? ScalarStyle.SingleQuoted : ScalarStyle.DoubleQuoted; + } + if (string.IsNullOrEmpty(scalarData.Value) && (flowLevel != 0 || isSimpleKeyContext)) + { + style = ScalarStyle.SingleQuoted; + } + if (noTag && !scalar.IsPlainImplicit) + { + style = ScalarStyle.SingleQuoted; + } + } + + if (style == ScalarStyle.SingleQuoted) + { + if (!scalarData.IsSingleQuotedAllowed) + { + style = ScalarStyle.DoubleQuoted; + } + } + + if (style == ScalarStyle.Literal || style == ScalarStyle.Folded) + { + if (!scalarData.IsBlockAllowed || flowLevel != 0 || isSimpleKeyContext) + { + style = ScalarStyle.DoubleQuoted; + } + } + + scalarData.Style = style; + } + + private void ProcessScalar() + { + switch (scalarData.Style) + { + case ScalarStyle.Plain: + WritePlainScalar(scalarData.Value, !isSimpleKeyContext); + break; + + case ScalarStyle.SingleQuoted: + WriteSingleQuotedScalar(scalarData.Value, !isSimpleKeyContext); + break; + + case ScalarStyle.DoubleQuoted: + WriteDoubleQuotedScalar(scalarData.Value, !isSimpleKeyContext); + break; + + case ScalarStyle.Literal: + WriteLiteralScalar(scalarData.Value); + break; + + case ScalarStyle.Folded: + WriteFoldedScalar(scalarData.Value); + break; + + default: + throw new InvalidOperationException(); + } + } + + #region Write scalar Methods + + private void WritePlainScalar(string value, bool allowBreaks) + { + if (!isWhitespace) + { + Write(' '); + } + + var previousSpace = false; + var previousBreak = false; + for (var index = 0; index < value.Length; ++index) + { + var character = value[index]; + if (IsSpace(character)) + { + if (allowBreaks && !previousSpace && column > bestWidth && index + 1 < value.Length && value[index + 1] != ' ') + { + WriteIndent(); + } + else + { + Write(character); + } + previousSpace = true; + } + else if (IsBreak(character, out var breakCharacter)) + { + if (!previousBreak && character == '\n') + { + WriteBreak(); + } + WriteBreak(breakCharacter); + isIndentation = true; + previousBreak = true; + } + else + { + if (previousBreak) + { + WriteIndent(); + } + Write(character); + isIndentation = false; + previousSpace = false; + previousBreak = false; + } + } + + isWhitespace = false; + isIndentation = false; + } + + private void WriteSingleQuotedScalar(string value, bool allowBreaks) + { + WriteIndicator("'", true, false, false); + + var previousSpace = false; + var previousBreak = false; + + for (var index = 0; index < value.Length; ++index) + { + var character = value[index]; + if (character == ' ') + { + if (allowBreaks && !previousSpace && column > bestWidth && index != 0 && index + 1 < value.Length && + value[index + 1] != ' ') + { + WriteIndent(); + } + else + { + Write(character); + } + previousSpace = true; + } + else if (IsBreak(character, out var breakCharacter)) + { + if (!previousBreak && character == '\n') + { + WriteBreak(); + } + WriteBreak(breakCharacter); + isIndentation = true; + previousBreak = true; + } + else + { + if (previousBreak) + { + WriteIndent(); + } + if (character == '\'') + { + Write(character); + } + Write(character); + isIndentation = false; + previousSpace = false; + previousBreak = false; + } + } + + WriteIndicator("'", false, false, false); + + isWhitespace = false; + isIndentation = false; + } + + private void WriteDoubleQuotedScalar(string value, bool allowBreaks) + { + WriteIndicator("\"", true, false, false); + + var previousSpace = false; + for (var index = 0; index < value.Length; ++index) + { + var character = value[index]; + if (!IsPrintable(character) || IsBreak(character, out _) || character == '"' || character == '\\') + { + Write('\\'); + + switch (character) + { + case '\0': + Write('0'); + break; + + case '\x7': + Write('a'); + break; + + case '\x8': + Write('b'); + break; + + case '\x9': + Write('t'); + break; + + case '\xA': + Write('n'); + break; + + case '\xB': + Write('v'); + break; + + case '\xC': + Write('f'); + break; + + case '\xD': + Write('r'); + break; + + case '\x1B': + Write('e'); + break; + + case '\x22': + Write('"'); + break; + + case '\x5C': + Write('\\'); + break; + + case '\x85': + Write('N'); + break; + + case '\xA0': + Write('_'); + break; + + case '\x2028': + Write('L'); + break; + + case '\x2029': + Write('P'); + break; + + default: + var code = (ushort)character; + if (code <= 0xFF) + { + Write('x'); + Write(code.ToString("X02", CultureInfo.InvariantCulture)); + } + else if (IsHighSurrogate(character)) + { + if (index + 1 < value.Length && IsLowSurrogate(value[index + 1])) + { + Write('U'); + Write(char.ConvertToUtf32(character, value[index + 1]).ToString("X08", CultureInfo.InvariantCulture)); + index++; + } + else + { + throw new SyntaxErrorException("While writing a quoted scalar, found an orphaned high surrogate."); + } + } + else + { + Write('u'); + Write(code.ToString("X04", CultureInfo.InvariantCulture)); + } + break; + } + previousSpace = false; + } + else if (character == ' ') + { + if (allowBreaks && !previousSpace && column > bestWidth && index > 0 && index + 1 < value.Length) + { + WriteIndent(); + if (value[index + 1] == ' ') + { + Write('\\'); + } + } + else + { + Write(character); + } + previousSpace = true; + } + else + { + Write(character); + previousSpace = false; + } + } + + WriteIndicator("\"", false, false, false); + + isWhitespace = false; + isIndentation = false; + } + + private void WriteLiteralScalar(string value) + { + var previousBreak = true; + + WriteIndicator("|", true, false, false); + WriteBlockScalarHints(value); + WriteBreak(); + + isIndentation = true; + isWhitespace = true; + + for (var i = 0; i < value.Length; ++i) + { + var character = value[i]; + if (character == '\r' && (i + 1) < value.Length && value[i + 1] == '\n') + { + continue; + } + + if (IsBreak(character, out var breakCharacter)) + { + WriteBreak(breakCharacter); + isIndentation = true; + previousBreak = true; + } + else + { + if (previousBreak) + { + WriteIndent(); + } + Write(character); + isIndentation = false; + previousBreak = false; + } + } + } + + private void WriteFoldedScalar(string value) + { + var previousBreak = true; + var leadingSpaces = true; + + WriteIndicator(">", true, false, false); + WriteBlockScalarHints(value); + WriteBreak(); + + isIndentation = true; + isWhitespace = true; + + for (var i = 0; i < value.Length; ++i) + { + var character = value[i]; + if (IsBreak(character, out var breakCharacter)) + { + if (character == '\r' && (i + 1) < value.Length && value[i + 1] == '\n') + { + continue; + } + + if (!previousBreak && !leadingSpaces && breakCharacter == '\n') + { + var k = 0; + while (i + k < value.Length && IsBreak(value[i + k], out _)) + { + ++k; + } + if (i + k < value.Length && !(IsBlank(value[i + k]) || IsBreak(value[i + k], out _))) + { + WriteBreak(); + } + } + + WriteBreak(breakCharacter); + isIndentation = true; + previousBreak = true; + } + else + { + if (previousBreak) + { + WriteIndent(); + leadingSpaces = IsBlank(character); + } + if (!previousBreak && character == ' ' && i + 1 < value.Length && value[i + 1] != ' ' && column > bestWidth) + { + WriteIndent(); + } + else + { + Write(character); + } + isIndentation = false; + previousBreak = false; + } + } + } + + // Todo: isn't this what CharacterAnalyser is for? + private static bool IsSpace(char character) + { + return character == ' '; + } + + private static bool IsBreak(char character, out char breakChar) + { + switch (character) + { + case '\r': + case '\n': + case '\x85': + breakChar = '\n'; + return true; + + case '\x2028': + case '\x2029': + breakChar = character; + return true; + + default: + breakChar = '\0'; + return false; + } + } + + private static bool IsBlank(char character) + { + return character == ' ' || character == '\t'; + } + + private static bool IsPrintable(char character) + { + return + character == '\x9' || + character == '\xA' || + character == '\xD' || + (character >= '\x20' && character <= '\x7E') || + character == '\x85' || + (character >= '\xA0' && character <= '\xD7FF') || + (character >= '\xE000' && character <= '\xFFFD'); + } + + private static bool IsHighSurrogate(char c) + { + return 0xD800 <= c && c <= 0xDBFF; + } + + private static bool IsLowSurrogate(char c) + { + return 0xDC00 <= c && c <= 0xDFFF; + } + + #endregion + + /// + /// Expect SEQUENCE-START. + /// + private void EmitSequenceStart(ParsingEvent evt) + { + ProcessAnchor(); + ProcessTag(); + + var sequenceStart = (SequenceStart)evt; + + if (flowLevel != 0 || isCanonical || sequenceStart.Style == SequenceStyle.Flow || CheckEmptySequence()) + { + state = EmitterState.FlowSequenceFirstItem; + } + else + { + state = EmitterState.BlockSequenceFirstItem; + } + } + + /// + /// Expect MAPPING-START. + /// + private void EmitMappingStart(ParsingEvent evt) + { + ProcessAnchor(); + ProcessTag(); + + var mappingStart = (MappingStart)evt; + + if (flowLevel != 0 || isCanonical || mappingStart.Style == MappingStyle.Flow || CheckEmptyMapping()) + { + state = EmitterState.FlowMappingFirstKey; + } + else + { + state = EmitterState.BlockMappingFirstKey; + } + } + + private void ProcessAnchor() + { + if (!anchorData.Anchor.IsEmpty && !skipAnchorName) + { + WriteIndicator(anchorData.IsAlias ? "*" : "&", true, false, false); + WriteAnchor(anchorData.Anchor); + } + } + + private void ProcessTag() + { + if (tagData.Handle == null && tagData.Suffix == null) + { + return; + } + + if (tagData.Handle != null) + { + WriteTagHandle(tagData.Handle); + if (tagData.Suffix != null) + { + WriteTagContent(tagData.Suffix, false); + } + } + else + { + WriteIndicator("!<", true, false, false); + WriteTagContent(tagData.Suffix!, false); + WriteIndicator(">", false, false, false); + } + } + + /// + /// Expect DOCUMENT-END. + /// + private void EmitDocumentEnd(ParsingEvent evt) + { + if (evt is DocumentEnd documentEnd) + { + WriteIndent(); + if (!documentEnd.IsImplicit) + { + WriteIndicator("...", true, false, false); + WriteIndent(); + isDocumentEndWritten = true; + } + + state = EmitterState.DocumentStart; + + tagDirectives.Clear(); + } + else + { + throw new YamlException("Expected DOCUMENT-END."); + } + } + + /// + /// Expect a flow item node. + /// + private void EmitFlowSequenceItem(ParsingEvent evt, bool isFirst) + { + if (isFirst) + { + WriteIndicator("[", true, true, false); + IncreaseIndent(true, false); + ++flowLevel; + } + + if (evt is SequenceEnd) + { + --flowLevel; + indent = indents.Pop(); + if (isCanonical && !isFirst) + { + WriteIndicator(",", false, false, false); + WriteIndent(); + } + WriteIndicator("]", false, false, false); + state = states.Pop(); + return; + } + + if (!isFirst) + { + WriteIndicator(",", false, false, false); + } + + if (isCanonical || column > bestWidth) + { + WriteIndent(); + } + + states.Push(EmitterState.FlowSequenceItem); + + EmitNode(evt, false, false); + } + + /// + /// Expect a flow key node. + /// + private void EmitFlowMappingKey(ParsingEvent evt, bool isFirst) + { + if (isFirst) + { + WriteIndicator("{", true, true, false); + IncreaseIndent(true, false); + ++flowLevel; + } + + if (evt is MappingEnd) + { + --flowLevel; + indent = indents.Pop(); + if (isCanonical && !isFirst) + { + WriteIndicator(",", false, false, false); + WriteIndent(); + } + WriteIndicator("}", false, false, false); + state = states.Pop(); + return; + } + + if (!isFirst) + { + WriteIndicator(",", false, false, false); + } + if (isCanonical || column > bestWidth) + { + WriteIndent(); + } + + if (!isCanonical && CheckSimpleKey()) + { + states.Push(EmitterState.FlowMappingSimpleValue); + EmitNode(evt, true, true); + } + else + { + WriteIndicator("?", true, false, false); + states.Push(EmitterState.FlowMappingValue); + EmitNode(evt, true, false); + } + } + + /// + /// Expect a flow value node. + /// + private void EmitFlowMappingValue(ParsingEvent evt, bool isSimple) + { + if (isSimple) + { + WriteIndicator(":", false, false, false); + } + else + { + if (isCanonical || column > bestWidth) + { + WriteIndent(); + } + WriteIndicator(":", true, false, false); + } + states.Push(EmitterState.FlowMappingKey); + EmitNode(evt, true, false); + } + + /// + /// Expect a block item node. + /// + private void EmitBlockSequenceItem(ParsingEvent evt, bool isFirst) + { + if (isFirst) + { + IncreaseIndent(false, (isMappingContext && !isIndentation)); + } + + if (evt is SequenceEnd) + { + indent = indents.Pop(); + state = states.Pop(); + return; + } + + WriteIndent(); + WriteIndicator("-", true, false, true); + states.Push(EmitterState.BlockSequenceItem); + + EmitNode(evt, false, false); + } + + /// + /// Expect a block key node. + /// + private void EmitBlockMappingKey(ParsingEvent evt, bool isFirst) + { + if (isFirst) + { + IncreaseIndent(false, false); + } + + if (evt is MappingEnd) + { + indent = indents.Pop(); + state = states.Pop(); + return; + } + + WriteIndent(); + + if (CheckSimpleKey()) + { + states.Push(EmitterState.BlockMappingSimpleValue); + EmitNode(evt, true, true); + } + else + { + WriteIndicator("?", true, false, true); + states.Push(EmitterState.BlockMappingValue); + EmitNode(evt, true, false); + } + } + + /// + /// Expect a block value node. + /// + private void EmitBlockMappingValue(ParsingEvent evt, bool isSimple) + { + if (isSimple) + { + WriteIndicator(":", false, false, false); + } + else + { + WriteIndent(); + WriteIndicator(":", true, false, true); + } + states.Push(EmitterState.BlockMappingKey); + EmitNode(evt, true, false); + } + + private void IncreaseIndent(bool isFlow, bool isIndentless) + { + indents.Push(indent); + + if (indent < 0) + { + indent = isFlow ? bestIndent : 0; + } + else if (!isIndentless || !forceIndentLess) + { + indent += bestIndent; + } + } + + #region Check Methods + + /// + /// Check if the document content is an empty scalar. + /// + private bool CheckEmptyDocument() + { + var index = 0; + foreach (var parsingEvent in events) + { + index++; + if (index == 2) + { + if (parsingEvent is Scalar scalar) + { + return string.IsNullOrEmpty(scalar.Value); + } + break; + } + } + + return false; + } + + /// + /// Check if the next node can be expressed as a simple key. + /// + private bool CheckSimpleKey() + { + if (events.Count < 1) + { + return false; + } + + int length; + switch (events.Peek().Type) + { + case EventType.Alias: + length = AnchorNameLength(anchorData.Anchor); + break; + + case EventType.Scalar: + if (scalarData.IsMultiline) + { + return false; + } + + length = + AnchorNameLength(anchorData.Anchor) + + SafeStringLength(tagData.Handle) + + SafeStringLength(tagData.Suffix) + + SafeStringLength(scalarData.Value); + break; + + case EventType.SequenceStart: + if (!CheckEmptySequence()) + { + return false; + } + length = + AnchorNameLength(anchorData.Anchor) + + SafeStringLength(tagData.Handle) + + SafeStringLength(tagData.Suffix); + break; + + case EventType.MappingStart: + if (!CheckEmptySequence()) + { + return false; + } + length = + AnchorNameLength(anchorData.Anchor) + + SafeStringLength(tagData.Handle) + + SafeStringLength(tagData.Suffix); + break; + + default: + return false; + } + + return length <= maxSimpleKeyLength; + } + + private int AnchorNameLength(AnchorName value) + { + return value.IsEmpty ? 0 : value.Value.Length; + } + + private int SafeStringLength(string? value) + { + return value == null ? 0 : value.Length; + } + + private bool CheckEmptySequence() => CheckEmptyStructure(); + private bool CheckEmptyMapping() => CheckEmptyStructure(); + + private bool CheckEmptyStructure() + where TStart : NodeEvent + where TEnd : ParsingEvent + { + if (events.Count < 2) + { + return false; + } + + using var enumerator = events.GetEnumerator(); + return enumerator.MoveNext() + && enumerator.Current is TStart + && enumerator.MoveNext() + && enumerator.Current is TEnd; + } + #endregion + + #region Write Methods + + private void WriteBlockScalarHints(string value) + { + var analyzer = new CharacterAnalyzer(new StringLookAheadBuffer(value)); + + if (analyzer.IsSpace() || analyzer.IsBreak()) + { + var indentHint = bestIndent.ToString(CultureInfo.InvariantCulture); + WriteIndicator(indentHint, false, false, false); + } + + string? chompHint = null; + if (value.Length == 0 || !analyzer.IsBreak(value.Length - 1)) + { + chompHint = "-"; + } + else if (value.Length >= 2 && analyzer.IsBreak(value.Length - 2)) + { + chompHint = "+"; + } + + if (chompHint != null) + { + WriteIndicator(chompHint, false, false, false); + } + } + + private void WriteIndicator(string indicator, bool needWhitespace, bool whitespace, bool indentation) + { + if (needWhitespace && !isWhitespace) + { + Write(' '); + } + + Write(indicator); + + isWhitespace = whitespace; + isIndentation &= indentation; + } + + private void WriteIndent() + { + var currentIndent = Math.Max(indent, 0); + + var isBreakRequired = !isIndentation + || column > currentIndent + || (column == currentIndent && !isWhitespace); + + if (isBreakRequired) + { + WriteBreak(); + } + + while (column < currentIndent) + { + Write(' '); + } + + isWhitespace = true; + isIndentation = true; + } + + private void WriteAnchor(AnchorName value) + { + Write(value.Value); + + isWhitespace = false; + isIndentation = false; + } + + private void WriteTagHandle(string value) + { + if (!isWhitespace) + { + Write(' '); + } + + Write(value); + + isWhitespace = false; + isIndentation = false; + } + + private void WriteTagContent(string value, bool needsWhitespace) + { + if (needsWhitespace && !isWhitespace) + { + Write(' '); + } + + Write(UrlEncode(value)); + + isWhitespace = false; + isIndentation = false; + } + + private static string UrlEncode(string text) + { + return UriReplacer.Replace(text, delegate (Match match) + { + using var bufferBuilder = StringBuilderPool.Rent(); + var buffer = bufferBuilder.Builder; + foreach (var toEncode in Encoding.UTF8.GetBytes(match.Value)) + { + buffer.AppendFormat("%{0:X02}", toEncode); + } + return buffer.ToString(); + }); + } + + private void Write(char value) + { + output.Write(value); + ++column; + } + + private void Write(string value) + { + output.Write(value); + column += value.Length; + } + + private void WriteBreak(char breakCharacter = '\n') + { + if (breakCharacter == '\n') + { + output.WriteLine(); + } + else + { + output.Write(breakCharacter); + } + column = 0; + } + + #endregion + } +} diff --git a/YamlDotNet/Core/EmitterSettings.cs b/YamlDotNet/Core/EmitterSettings.cs index 45fe31502..ee6c85302 100644 --- a/YamlDotNet/Core/EmitterSettings.cs +++ b/YamlDotNet/Core/EmitterSettings.cs @@ -1,159 +1,159 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - public sealed class EmitterSettings - { - /// - /// The preferred indentation. - /// - public int BestIndent { get; } = 2; - - /// - /// The preferred text width. - /// - public int BestWidth { get; } = int.MaxValue; - - /// - /// If true, write the output in canonical form. - /// - public bool IsCanonical { get; } = false; - - /// - /// If true, write output without anchor names. - /// - public bool SkipAnchorName { get; private set; } - - /// - /// The maximum allowed length for simple keys. - /// - /// - /// The specifiction mandates 1024 characters, but any desired value may be used. - /// - public int MaxSimpleKeyLength { get; } = 1024; - - /// - /// Indent sequences. The default is to not indent. - /// - public bool IndentSequences { get; } = false; - - public static readonly EmitterSettings Default = new EmitterSettings(); - - public EmitterSettings() - { - } - - public EmitterSettings(int bestIndent, int bestWidth, bool isCanonical, int maxSimpleKeyLength, bool skipAnchorName = false, bool indentSequences = false) - { - if (bestIndent < 2 || bestIndent > 9) - { - throw new ArgumentOutOfRangeException(nameof(bestIndent), $"BestIndent must be between 2 and 9, inclusive"); - } - - if (bestWidth <= bestIndent * 2) - { - throw new ArgumentOutOfRangeException(nameof(bestWidth), "BestWidth must be greater than BestIndent x 2."); - } - - if (maxSimpleKeyLength < 0) - { - throw new ArgumentOutOfRangeException(nameof(maxSimpleKeyLength), "MaxSimpleKeyLength must be >= 0"); - } - - BestIndent = bestIndent; - BestWidth = bestWidth; - IsCanonical = isCanonical; - MaxSimpleKeyLength = maxSimpleKeyLength; - SkipAnchorName = skipAnchorName; - IndentSequences = indentSequences; - } - - public EmitterSettings WithBestIndent(int bestIndent) - { - return new EmitterSettings( - bestIndent, - BestWidth, - IsCanonical, - MaxSimpleKeyLength, - SkipAnchorName - ); - } - - public EmitterSettings WithBestWidth(int bestWidth) - { - return new EmitterSettings( - BestIndent, - bestWidth, - IsCanonical, - MaxSimpleKeyLength, - SkipAnchorName - ); - } - - public EmitterSettings WithMaxSimpleKeyLength(int maxSimpleKeyLength) - { - return new EmitterSettings( - BestIndent, - BestWidth, - IsCanonical, - maxSimpleKeyLength, - SkipAnchorName - ); - } - - public EmitterSettings Canonical() - { - return new EmitterSettings( - BestIndent, - BestWidth, - true, - MaxSimpleKeyLength, - SkipAnchorName - ); - } - - public EmitterSettings WithoutAnchorName() - { - return new EmitterSettings( - BestIndent, - BestWidth, - IsCanonical, - MaxSimpleKeyLength, - true - ); - } - - public EmitterSettings WithIndentedSequences() - { - return new EmitterSettings( - BestIndent, - BestWidth, - IsCanonical, - MaxSimpleKeyLength, - SkipAnchorName, - true - ); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + public sealed class EmitterSettings + { + /// + /// The preferred indentation. + /// + public int BestIndent { get; } = 2; + + /// + /// The preferred text width. + /// + public int BestWidth { get; } = int.MaxValue; + + /// + /// If true, write the output in canonical form. + /// + public bool IsCanonical { get; } = false; + + /// + /// If true, write output without anchor names. + /// + public bool SkipAnchorName { get; private set; } + + /// + /// The maximum allowed length for simple keys. + /// + /// + /// The specifiction mandates 1024 characters, but any desired value may be used. + /// + public int MaxSimpleKeyLength { get; } = 1024; + + /// + /// Indent sequences. The default is to not indent. + /// + public bool IndentSequences { get; } = false; + + public static readonly EmitterSettings Default = new EmitterSettings(); + + public EmitterSettings() + { + } + + public EmitterSettings(int bestIndent, int bestWidth, bool isCanonical, int maxSimpleKeyLength, bool skipAnchorName = false, bool indentSequences = false) + { + if (bestIndent < 2 || bestIndent > 9) + { + throw new ArgumentOutOfRangeException(nameof(bestIndent), $"BestIndent must be between 2 and 9, inclusive"); + } + + if (bestWidth <= bestIndent * 2) + { + throw new ArgumentOutOfRangeException(nameof(bestWidth), "BestWidth must be greater than BestIndent x 2."); + } + + if (maxSimpleKeyLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(maxSimpleKeyLength), "MaxSimpleKeyLength must be >= 0"); + } + + BestIndent = bestIndent; + BestWidth = bestWidth; + IsCanonical = isCanonical; + MaxSimpleKeyLength = maxSimpleKeyLength; + SkipAnchorName = skipAnchorName; + IndentSequences = indentSequences; + } + + public EmitterSettings WithBestIndent(int bestIndent) + { + return new EmitterSettings( + bestIndent, + BestWidth, + IsCanonical, + MaxSimpleKeyLength, + SkipAnchorName + ); + } + + public EmitterSettings WithBestWidth(int bestWidth) + { + return new EmitterSettings( + BestIndent, + bestWidth, + IsCanonical, + MaxSimpleKeyLength, + SkipAnchorName + ); + } + + public EmitterSettings WithMaxSimpleKeyLength(int maxSimpleKeyLength) + { + return new EmitterSettings( + BestIndent, + BestWidth, + IsCanonical, + maxSimpleKeyLength, + SkipAnchorName + ); + } + + public EmitterSettings Canonical() + { + return new EmitterSettings( + BestIndent, + BestWidth, + true, + MaxSimpleKeyLength, + SkipAnchorName + ); + } + + public EmitterSettings WithoutAnchorName() + { + return new EmitterSettings( + BestIndent, + BestWidth, + IsCanonical, + MaxSimpleKeyLength, + true + ); + } + + public EmitterSettings WithIndentedSequences() + { + return new EmitterSettings( + BestIndent, + BestWidth, + IsCanonical, + MaxSimpleKeyLength, + SkipAnchorName, + true + ); + } + } +} diff --git a/YamlDotNet/Core/EmitterState.cs b/YamlDotNet/Core/EmitterState.cs index 4992aaefc..45ba5aed4 100644 --- a/YamlDotNet/Core/EmitterState.cs +++ b/YamlDotNet/Core/EmitterState.cs @@ -1,45 +1,45 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core -{ - internal enum EmitterState - { - StreamStart, - StreamEnd, - FirstDocumentStart, - DocumentStart, - DocumentContent, - DocumentEnd, - FlowSequenceFirstItem, - FlowSequenceItem, - FlowMappingFirstKey, - FlowMappingKey, - FlowMappingSimpleValue, - FlowMappingValue, - BlockSequenceFirstItem, - BlockSequenceItem, - BlockMappingFirstKey, - BlockMappingKey, - BlockMappingSimpleValue, - BlockMappingValue - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core +{ + internal enum EmitterState + { + StreamStart, + StreamEnd, + FirstDocumentStart, + DocumentStart, + DocumentContent, + DocumentEnd, + FlowSequenceFirstItem, + FlowSequenceItem, + FlowMappingFirstKey, + FlowMappingKey, + FlowMappingSimpleValue, + FlowMappingValue, + BlockSequenceFirstItem, + BlockSequenceItem, + BlockMappingFirstKey, + BlockMappingKey, + BlockMappingSimpleValue, + BlockMappingValue + } +} diff --git a/YamlDotNet/Core/Events/AnchorAlias.cs b/YamlDotNet/Core/Events/AnchorAlias.cs index 0a420eee2..2ed9a1cb4 100644 --- a/YamlDotNet/Core/Events/AnchorAlias.cs +++ b/YamlDotNet/Core/Events/AnchorAlias.cs @@ -1,85 +1,85 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents an alias event. - /// - public sealed class AnchorAlias : ParsingEvent - { - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type => EventType.Alias; - - /// - /// Gets the value of the alias. - /// - public AnchorName Value { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The value of the alias. - /// The start position of the event. - /// The end position of the event. - public AnchorAlias(AnchorName value, Mark start, Mark end) - : base(start, end) - { - if (value.IsEmpty) - { - throw new YamlException(start, end, "Anchor value must not be empty."); - } - - this.Value = value; - } - - /// - /// Initializes a new instance of the class. - /// - /// The value of the alias. - public AnchorAlias(AnchorName value) - : this(value, Mark.Empty, Mark.Empty) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return $"Alias [value = {Value}]"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents an alias event. + /// + public sealed class AnchorAlias : ParsingEvent + { + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type => EventType.Alias; + + /// + /// Gets the value of the alias. + /// + public AnchorName Value { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The value of the alias. + /// The start position of the event. + /// The end position of the event. + public AnchorAlias(AnchorName value, Mark start, Mark end) + : base(start, end) + { + if (value.IsEmpty) + { + throw new YamlException(start, end, "Anchor value must not be empty."); + } + + this.Value = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The value of the alias. + public AnchorAlias(AnchorName value) + : this(value, Mark.Empty, Mark.Empty) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return $"Alias [value = {Value}]"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/Events/Comment.cs b/YamlDotNet/Core/Events/Comment.cs index 388f09cab..907d25810 100644 --- a/YamlDotNet/Core/Events/Comment.cs +++ b/YamlDotNet/Core/Events/Comment.cs @@ -1,59 +1,59 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - public sealed class Comment : ParsingEvent - { - public string Value { get; } - public bool IsInline { get; } - - public Comment(string value, bool isInline) - : this(value, isInline, Mark.Empty, Mark.Empty) - { - } - - public Comment(string value, bool isInline, Mark start, Mark end) - : base(start, end) - { - Value = value; - IsInline = isInline; - } - - internal override EventType Type => EventType.Comment; - - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return $"{(IsInline ? "Inline" : "Block")} Comment [{Value}]"; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + public sealed class Comment : ParsingEvent + { + public string Value { get; } + public bool IsInline { get; } + + public Comment(string value, bool isInline) + : this(value, isInline, Mark.Empty, Mark.Empty) + { + } + + public Comment(string value, bool isInline, Mark start, Mark end) + : base(start, end) + { + Value = value; + IsInline = isInline; + } + + internal override EventType Type => EventType.Comment; + + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return $"{(IsInline ? "Inline" : "Block")} Comment [{Value}]"; + } + } +} diff --git a/YamlDotNet/Core/Events/DocumentEnd.cs b/YamlDotNet/Core/Events/DocumentEnd.cs index 0d8c40c53..e802e627c 100644 --- a/YamlDotNet/Core/Events/DocumentEnd.cs +++ b/YamlDotNet/Core/Events/DocumentEnd.cs @@ -1,90 +1,90 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents a document end event. - /// - public sealed class DocumentEnd : ParsingEvent - { - /// - /// Gets a value indicating the variation of depth caused by this event. - /// The value can be either -1, 0 or 1. For start events, it will be 1, - /// for end events, it will be -1, and for the remaining events, it will be 0. - /// - public override int NestingIncrease => -1; - - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type => EventType.DocumentEnd; - - /// - /// Gets a value indicating whether this instance is implicit. - /// - /// - /// true if this instance is implicit; otherwise, false. - /// - public bool IsImplicit { get; } - - /// - /// Initializes a new instance of the class. - /// - /// Indicates whether the event is implicit. - /// The start position of the event. - /// The end position of the event. - public DocumentEnd(bool isImplicit, Mark start, Mark end) - : base(start, end) - { - this.IsImplicit = isImplicit; - } - - /// - /// Initializes a new instance of the class. - /// - /// Indicates whether the event is implicit. - public DocumentEnd(bool isImplicit) - : this(isImplicit, Mark.Empty, Mark.Empty) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return $"Document end [isImplicit = {IsImplicit}]"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents a document end event. + /// + public sealed class DocumentEnd : ParsingEvent + { + /// + /// Gets a value indicating the variation of depth caused by this event. + /// The value can be either -1, 0 or 1. For start events, it will be 1, + /// for end events, it will be -1, and for the remaining events, it will be 0. + /// + public override int NestingIncrease => -1; + + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type => EventType.DocumentEnd; + + /// + /// Gets a value indicating whether this instance is implicit. + /// + /// + /// true if this instance is implicit; otherwise, false. + /// + public bool IsImplicit { get; } + + /// + /// Initializes a new instance of the class. + /// + /// Indicates whether the event is implicit. + /// The start position of the event. + /// The end position of the event. + public DocumentEnd(bool isImplicit, Mark start, Mark end) + : base(start, end) + { + this.IsImplicit = isImplicit; + } + + /// + /// Initializes a new instance of the class. + /// + /// Indicates whether the event is implicit. + public DocumentEnd(bool isImplicit) + : this(isImplicit, Mark.Empty, Mark.Empty) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return $"Document end [isImplicit = {IsImplicit}]"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/Events/DocumentStart.cs b/YamlDotNet/Core/Events/DocumentStart.cs index 8725eeb06..e737c83e3 100644 --- a/YamlDotNet/Core/Events/DocumentStart.cs +++ b/YamlDotNet/Core/Events/DocumentStart.cs @@ -1,128 +1,128 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core.Tokens; - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents a document start event. - /// - public sealed class DocumentStart : ParsingEvent - { - /// - /// Gets a value indicating the variation of depth caused by this event. - /// The value can be either -1, 0 or 1. For start events, it will be 1, - /// for end events, it will be -1, and for the remaining events, it will be 0. - /// - public override int NestingIncrease => 1; - - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type => EventType.DocumentStart; - - /// - /// Gets the tags. - /// - /// The tags. - public TagDirectiveCollection? Tags { get; } - - /// - /// Gets the version. - /// - /// The version. - public VersionDirective? Version { get; } - - /// - /// Gets a value indicating whether this instance is implicit. - /// - /// - /// true if this instance is implicit; otherwise, false. - /// - public bool IsImplicit { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The version. - /// The tags. - /// Indicates whether the event is implicit. - /// The start position of the event. - /// The end position of the event. - public DocumentStart(VersionDirective? version, TagDirectiveCollection? tags, bool isImplicit, Mark start, Mark end) - : base(start, end) - { - this.Version = version; - this.Tags = tags; - this.IsImplicit = isImplicit; - } - - /// - /// Initializes a new instance of the class. - /// - /// The version. - /// The tags. - /// Indicates whether the event is implicit. - public DocumentStart(VersionDirective? version, TagDirectiveCollection? tags, bool isImplicit) - : this(version, tags, isImplicit, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the event. - /// The end position of the event. - public DocumentStart(in Mark start, in Mark end) - : this(null, null, true, start, end) - { - } - - /// - /// Initializes a new instance of the class. - /// - public DocumentStart() - : this(null, null, true, Mark.Empty, Mark.Empty) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return $"Document start [isImplicit = {IsImplicit}]"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core.Tokens; + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents a document start event. + /// + public sealed class DocumentStart : ParsingEvent + { + /// + /// Gets a value indicating the variation of depth caused by this event. + /// The value can be either -1, 0 or 1. For start events, it will be 1, + /// for end events, it will be -1, and for the remaining events, it will be 0. + /// + public override int NestingIncrease => 1; + + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type => EventType.DocumentStart; + + /// + /// Gets the tags. + /// + /// The tags. + public TagDirectiveCollection? Tags { get; } + + /// + /// Gets the version. + /// + /// The version. + public VersionDirective? Version { get; } + + /// + /// Gets a value indicating whether this instance is implicit. + /// + /// + /// true if this instance is implicit; otherwise, false. + /// + public bool IsImplicit { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The version. + /// The tags. + /// Indicates whether the event is implicit. + /// The start position of the event. + /// The end position of the event. + public DocumentStart(VersionDirective? version, TagDirectiveCollection? tags, bool isImplicit, Mark start, Mark end) + : base(start, end) + { + this.Version = version; + this.Tags = tags; + this.IsImplicit = isImplicit; + } + + /// + /// Initializes a new instance of the class. + /// + /// The version. + /// The tags. + /// Indicates whether the event is implicit. + public DocumentStart(VersionDirective? version, TagDirectiveCollection? tags, bool isImplicit) + : this(version, tags, isImplicit, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the event. + /// The end position of the event. + public DocumentStart(in Mark start, in Mark end) + : this(null, null, true, start, end) + { + } + + /// + /// Initializes a new instance of the class. + /// + public DocumentStart() + : this(null, null, true, Mark.Empty, Mark.Empty) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return $"Document start [isImplicit = {IsImplicit}]"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/Events/EventType.cs b/YamlDotNet/Core/Events/EventType.cs index ce2a6068c..944131e7c 100644 --- a/YamlDotNet/Core/Events/EventType.cs +++ b/YamlDotNet/Core/Events/EventType.cs @@ -1,39 +1,39 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - internal enum EventType - { - None, - StreamStart, - StreamEnd, - DocumentStart, - DocumentEnd, - Alias, - Scalar, - SequenceStart, - SequenceEnd, - MappingStart, - MappingEnd, - Comment, - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + internal enum EventType + { + None, + StreamStart, + StreamEnd, + DocumentStart, + DocumentEnd, + Alias, + Scalar, + SequenceStart, + SequenceEnd, + MappingStart, + MappingEnd, + Comment, + } +} diff --git a/YamlDotNet/Core/Events/IParsingEventVisitor.cs b/YamlDotNet/Core/Events/IParsingEventVisitor.cs index 2161c28c2..afe036025 100644 --- a/YamlDotNet/Core/Events/IParsingEventVisitor.cs +++ b/YamlDotNet/Core/Events/IParsingEventVisitor.cs @@ -1,41 +1,41 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Callback interface for external event Visitor. - /// - public interface IParsingEventVisitor - { - void Visit(AnchorAlias e); - void Visit(StreamStart e); - void Visit(StreamEnd e); - void Visit(DocumentStart e); - void Visit(DocumentEnd e); - void Visit(Scalar e); - void Visit(SequenceStart e); - void Visit(SequenceEnd e); - void Visit(MappingStart e); - void Visit(MappingEnd e); - void Visit(Comment e); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Callback interface for external event Visitor. + /// + public interface IParsingEventVisitor + { + void Visit(AnchorAlias e); + void Visit(StreamStart e); + void Visit(StreamEnd e); + void Visit(DocumentStart e); + void Visit(DocumentEnd e); + void Visit(Scalar e); + void Visit(SequenceStart e); + void Visit(SequenceEnd e); + void Visit(MappingStart e); + void Visit(MappingEnd e); + void Visit(Comment e); + } +} diff --git a/YamlDotNet/Core/Events/MappingEnd.cs b/YamlDotNet/Core/Events/MappingEnd.cs index 8f083437a..a9fbbd1e8 100644 --- a/YamlDotNet/Core/Events/MappingEnd.cs +++ b/YamlDotNet/Core/Events/MappingEnd.cs @@ -1,79 +1,79 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents a mapping end event. - /// - public class MappingEnd : ParsingEvent - { - /// - /// Gets a value indicating the variation of depth caused by this event. - /// The value can be either -1, 0 or 1. For start events, it will be 1, - /// for end events, it will be -1, and for the remaining events, it will be 0. - /// - public override int NestingIncrease => -1; - - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type => EventType.MappingEnd; - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the event. - /// The end position of the event. - public MappingEnd(in Mark start, in Mark end) - : base(start, end) - { - } - - /// - /// Initializes a new instance of the class. - /// - public MappingEnd() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return "Mapping end"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents a mapping end event. + /// + public class MappingEnd : ParsingEvent + { + /// + /// Gets a value indicating the variation of depth caused by this event. + /// The value can be either -1, 0 or 1. For start events, it will be 1, + /// for end events, it will be -1, and for the remaining events, it will be 0. + /// + public override int NestingIncrease => -1; + + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type => EventType.MappingEnd; + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the event. + /// The end position of the event. + public MappingEnd(in Mark start, in Mark end) + : base(start, end) + { + } + + /// + /// Initializes a new instance of the class. + /// + public MappingEnd() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return "Mapping end"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/Events/MappingStart.cs b/YamlDotNet/Core/Events/MappingStart.cs index 46017ce09..701c551a9 100644 --- a/YamlDotNet/Core/Events/MappingStart.cs +++ b/YamlDotNet/Core/Events/MappingStart.cs @@ -1,116 +1,116 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents a mapping start event. - /// - public sealed class MappingStart : NodeEvent - { - /// - /// Gets a value indicating the variation of depth caused by this event. - /// The value can be either -1, 0 or 1. For start events, it will be 1, - /// for end events, it will be -1, and for the remaining events, it will be 0. - /// - public override int NestingIncrease => 1; - - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type => EventType.MappingStart; - - /// - /// Gets a value indicating whether this instance is implicit. - /// - /// - /// true if this instance is implicit; otherwise, false. - /// - public bool IsImplicit { get; } - - /// - /// Gets a value indicating whether this instance is canonical. - /// - /// - public override bool IsCanonical => !IsImplicit; - - /// - /// Gets the style of the mapping. - /// - public MappingStyle Style { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The anchor. - /// The tag. - /// Indicates whether the event is implicit. - /// The style of the mapping. - /// The start position of the event. - /// The end position of the event. - public MappingStart(AnchorName anchor, TagName tag, bool isImplicit, MappingStyle style, Mark start, Mark end) - : base(anchor, tag, start, end) - { - this.IsImplicit = isImplicit; - this.Style = style; - } - - /// - /// Initializes a new instance of the class. - /// - /// The anchor. - /// The tag. - /// Indicates whether the event is implicit. - /// The style of the mapping. - public MappingStart(AnchorName anchor, TagName tag, bool isImplicit, MappingStyle style) - : this(anchor, tag, isImplicit, style, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - public MappingStart() - : this(AnchorName.Empty, TagName.Empty, true, MappingStyle.Any, Mark.Empty, Mark.Empty) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return $"Mapping start [anchor = {Anchor}, tag = {Tag}, isImplicit = {IsImplicit}, style = {Style}]"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents a mapping start event. + /// + public sealed class MappingStart : NodeEvent + { + /// + /// Gets a value indicating the variation of depth caused by this event. + /// The value can be either -1, 0 or 1. For start events, it will be 1, + /// for end events, it will be -1, and for the remaining events, it will be 0. + /// + public override int NestingIncrease => 1; + + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type => EventType.MappingStart; + + /// + /// Gets a value indicating whether this instance is implicit. + /// + /// + /// true if this instance is implicit; otherwise, false. + /// + public bool IsImplicit { get; } + + /// + /// Gets a value indicating whether this instance is canonical. + /// + /// + public override bool IsCanonical => !IsImplicit; + + /// + /// Gets the style of the mapping. + /// + public MappingStyle Style { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The anchor. + /// The tag. + /// Indicates whether the event is implicit. + /// The style of the mapping. + /// The start position of the event. + /// The end position of the event. + public MappingStart(AnchorName anchor, TagName tag, bool isImplicit, MappingStyle style, Mark start, Mark end) + : base(anchor, tag, start, end) + { + this.IsImplicit = isImplicit; + this.Style = style; + } + + /// + /// Initializes a new instance of the class. + /// + /// The anchor. + /// The tag. + /// Indicates whether the event is implicit. + /// The style of the mapping. + public MappingStart(AnchorName anchor, TagName tag, bool isImplicit, MappingStyle style) + : this(anchor, tag, isImplicit, style, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + public MappingStart() + : this(AnchorName.Empty, TagName.Empty, true, MappingStyle.Any, Mark.Empty, Mark.Empty) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return $"Mapping start [anchor = {Anchor}, tag = {Tag}, isImplicit = {IsImplicit}, style = {Style}]"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/Events/MappingStyle.cs b/YamlDotNet/Core/Events/MappingStyle.cs index bd55b998f..f6b6a89a8 100644 --- a/YamlDotNet/Core/Events/MappingStyle.cs +++ b/YamlDotNet/Core/Events/MappingStyle.cs @@ -1,44 +1,44 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Specifies the style of a mapping. - /// - public enum MappingStyle - { - /// - /// Let the emitter choose the style. - /// - Any, - - /// - /// The block mapping style. - /// - Block, - - /// - /// The flow mapping style. - /// - Flow - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Specifies the style of a mapping. + /// + public enum MappingStyle + { + /// + /// Let the emitter choose the style. + /// + Any, + + /// + /// The block mapping style. + /// + Block, + + /// + /// The flow mapping style. + /// + Flow + } +} diff --git a/YamlDotNet/Core/Events/NodeEvent.cs b/YamlDotNet/Core/Events/NodeEvent.cs index 614812556..331dbc0a6 100644 --- a/YamlDotNet/Core/Events/NodeEvent.cs +++ b/YamlDotNet/Core/Events/NodeEvent.cs @@ -1,72 +1,72 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Contains the behavior that is common between node events. - /// - public abstract class NodeEvent : ParsingEvent - { - /// - /// Gets the anchor. - /// - /// - public AnchorName Anchor { get; } - - /// - /// Gets the tag. - /// - /// - public TagName Tag { get; } - - /// - /// Gets a value indicating whether this instance is canonical. - /// - /// - public abstract bool IsCanonical - { - get; - } - - /// - /// Initializes a new instance of the class. - /// - /// The anchor. - /// The tag. - /// The start position of the event. - /// The end position of the event. - protected NodeEvent(AnchorName anchor, TagName tag, Mark start, Mark end) - : base(start, end) - { - this.Anchor = anchor; - this.Tag = tag; - } - - /// - /// Initializes a new instance of the class. - /// - protected NodeEvent(AnchorName anchor, TagName tag) - : this(anchor, tag, Mark.Empty, Mark.Empty) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Contains the behavior that is common between node events. + /// + public abstract class NodeEvent : ParsingEvent + { + /// + /// Gets the anchor. + /// + /// + public AnchorName Anchor { get; } + + /// + /// Gets the tag. + /// + /// + public TagName Tag { get; } + + /// + /// Gets a value indicating whether this instance is canonical. + /// + /// + public abstract bool IsCanonical + { + get; + } + + /// + /// Initializes a new instance of the class. + /// + /// The anchor. + /// The tag. + /// The start position of the event. + /// The end position of the event. + protected NodeEvent(AnchorName anchor, TagName tag, Mark start, Mark end) + : base(start, end) + { + this.Anchor = anchor; + this.Tag = tag; + } + + /// + /// Initializes a new instance of the class. + /// + protected NodeEvent(AnchorName anchor, TagName tag) + : this(anchor, tag, Mark.Empty, Mark.Empty) + { + } + } +} diff --git a/YamlDotNet/Core/Events/ParsingEvent.cs b/YamlDotNet/Core/Events/ParsingEvent.cs index ba6a3172e..e8d11cb52 100644 --- a/YamlDotNet/Core/Events/ParsingEvent.cs +++ b/YamlDotNet/Core/Events/ParsingEvent.cs @@ -1,68 +1,68 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Base class for parsing events. - /// - public abstract class ParsingEvent - { - /// - /// Gets a value indicating the variation of depth caused by this event. - /// The value can be either -1, 0 or 1. For start events, it will be 1, - /// for end events, it will be -1, and for the remaining events, it will be 0. - /// - public virtual int NestingIncrease => 0; - - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal abstract EventType Type { get; } - - /// - /// Gets the position in the input stream where the event starts. - /// - public Mark Start { get; } - - /// - /// Gets the position in the input stream where the event ends. - /// - public Mark End { get; } - - /// - /// Accepts the specified visitor. - /// - /// Visitor to accept, may not be null - public abstract void Accept(IParsingEventVisitor visitor); - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the event. - /// The end position of the event. - internal ParsingEvent(in Mark start, in Mark end) - { - this.Start = start; - this.End = end; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Base class for parsing events. + /// + public abstract class ParsingEvent + { + /// + /// Gets a value indicating the variation of depth caused by this event. + /// The value can be either -1, 0 or 1. For start events, it will be 1, + /// for end events, it will be -1, and for the remaining events, it will be 0. + /// + public virtual int NestingIncrease => 0; + + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal abstract EventType Type { get; } + + /// + /// Gets the position in the input stream where the event starts. + /// + public Mark Start { get; } + + /// + /// Gets the position in the input stream where the event ends. + /// + public Mark End { get; } + + /// + /// Accepts the specified visitor. + /// + /// Visitor to accept, may not be null + public abstract void Accept(IParsingEventVisitor visitor); + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the event. + /// The end position of the event. + internal ParsingEvent(in Mark start, in Mark end) + { + this.Start = start; + this.End = end; + } + } +} diff --git a/YamlDotNet/Core/Events/Scalar.cs b/YamlDotNet/Core/Events/Scalar.cs index 56a81d3fa..d62d2ec31 100644 --- a/YamlDotNet/Core/Events/Scalar.cs +++ b/YamlDotNet/Core/Events/Scalar.cs @@ -1,150 +1,150 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents a scalar event. - /// - public sealed class Scalar : NodeEvent - { - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type => EventType.Scalar; - - /// - /// Gets the value. - /// - /// The value. - public string Value { get; } - - /// - /// Gets the style of the scalar. - /// - /// The style. - public ScalarStyle Style { get; } - - /// - /// Gets a value indicating whether the tag is optional for the plain style. - /// - public bool IsPlainImplicit { get; } - - /// - /// Gets a value indicating whether the tag is optional for any non-plain style. - /// - public bool IsQuotedImplicit { get; } - - /// - /// Gets a value indicating whether this instance is canonical. - /// - /// - public override bool IsCanonical => !IsPlainImplicit && !IsQuotedImplicit; - - /// - /// Gets whether this scalar event is a key - /// - public bool IsKey { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The anchor. - /// The tag. - /// The value. - /// The style. - /// . - /// . - /// The start position of the event. - /// The end position of the event. - /// Whether or not this scalar event is for a key - public Scalar(AnchorName anchor, TagName tag, string value, ScalarStyle style, bool isPlainImplicit, bool isQuotedImplicit, Mark start, Mark end, bool isKey = false) - : base(anchor, tag, start, end) - { - this.Value = value; - this.Style = style; - this.IsPlainImplicit = isPlainImplicit; - this.IsQuotedImplicit = isQuotedImplicit; - this.IsKey = isKey; - } - - /// - /// Initializes a new instance of the class. - /// - /// The anchor. - /// The tag. - /// The value. - /// The style. - /// . - /// . - public Scalar(AnchorName anchor, TagName tag, string value, ScalarStyle style, bool isPlainImplicit, bool isQuotedImplicit) - : this(anchor, tag, value, style, isPlainImplicit, isQuotedImplicit, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The value. - public Scalar(string value) - : this(AnchorName.Empty, TagName.Empty, value, ScalarStyle.Any, true, true, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The tag. - /// The value. - public Scalar(TagName tag, string value) - : this(AnchorName.Empty, tag, value, ScalarStyle.Any, true, true, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - public Scalar(AnchorName anchor, TagName tag, string value) - : this(anchor, tag, value, ScalarStyle.Any, true, true, Mark.Empty, Mark.Empty) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return $"Scalar [anchor = {Anchor}, tag = {Tag}, value = {Value}, style = {Style}, isPlainImplicit = {IsPlainImplicit}, isQuotedImplicit = {IsQuotedImplicit}]"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents a scalar event. + /// + public sealed class Scalar : NodeEvent + { + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type => EventType.Scalar; + + /// + /// Gets the value. + /// + /// The value. + public string Value { get; } + + /// + /// Gets the style of the scalar. + /// + /// The style. + public ScalarStyle Style { get; } + + /// + /// Gets a value indicating whether the tag is optional for the plain style. + /// + public bool IsPlainImplicit { get; } + + /// + /// Gets a value indicating whether the tag is optional for any non-plain style. + /// + public bool IsQuotedImplicit { get; } + + /// + /// Gets a value indicating whether this instance is canonical. + /// + /// + public override bool IsCanonical => !IsPlainImplicit && !IsQuotedImplicit; + + /// + /// Gets whether this scalar event is a key + /// + public bool IsKey { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The anchor. + /// The tag. + /// The value. + /// The style. + /// . + /// . + /// The start position of the event. + /// The end position of the event. + /// Whether or not this scalar event is for a key + public Scalar(AnchorName anchor, TagName tag, string value, ScalarStyle style, bool isPlainImplicit, bool isQuotedImplicit, Mark start, Mark end, bool isKey = false) + : base(anchor, tag, start, end) + { + this.Value = value; + this.Style = style; + this.IsPlainImplicit = isPlainImplicit; + this.IsQuotedImplicit = isQuotedImplicit; + this.IsKey = isKey; + } + + /// + /// Initializes a new instance of the class. + /// + /// The anchor. + /// The tag. + /// The value. + /// The style. + /// . + /// . + public Scalar(AnchorName anchor, TagName tag, string value, ScalarStyle style, bool isPlainImplicit, bool isQuotedImplicit) + : this(anchor, tag, value, style, isPlainImplicit, isQuotedImplicit, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public Scalar(string value) + : this(AnchorName.Empty, TagName.Empty, value, ScalarStyle.Any, true, true, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The tag. + /// The value. + public Scalar(TagName tag, string value) + : this(AnchorName.Empty, tag, value, ScalarStyle.Any, true, true, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + public Scalar(AnchorName anchor, TagName tag, string value) + : this(anchor, tag, value, ScalarStyle.Any, true, true, Mark.Empty, Mark.Empty) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return $"Scalar [anchor = {Anchor}, tag = {Tag}, value = {Value}, style = {Style}, isPlainImplicit = {IsPlainImplicit}, isQuotedImplicit = {IsQuotedImplicit}]"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/Events/SequenceEnd.cs b/YamlDotNet/Core/Events/SequenceEnd.cs index 217d21170..60a61ff3c 100644 --- a/YamlDotNet/Core/Events/SequenceEnd.cs +++ b/YamlDotNet/Core/Events/SequenceEnd.cs @@ -1,79 +1,79 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents a sequence end event. - /// - public sealed class SequenceEnd : ParsingEvent - { - /// - /// Gets a value indicating the variation of depth caused by this event. - /// The value can be either -1, 0 or 1. For start events, it will be 1, - /// for end events, it will be -1, and for the remaining events, it will be 0. - /// - public override int NestingIncrease => -1; - - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type => EventType.SequenceEnd; - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the event. - /// The end position of the event. - public SequenceEnd(in Mark start, in Mark end) - : base(start, end) - { - } - - /// - /// Initializes a new instance of the class. - /// - public SequenceEnd() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return "Sequence end"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents a sequence end event. + /// + public sealed class SequenceEnd : ParsingEvent + { + /// + /// Gets a value indicating the variation of depth caused by this event. + /// The value can be either -1, 0 or 1. For start events, it will be 1, + /// for end events, it will be -1, and for the remaining events, it will be 0. + /// + public override int NestingIncrease => -1; + + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type => EventType.SequenceEnd; + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the event. + /// The end position of the event. + public SequenceEnd(in Mark start, in Mark end) + : base(start, end) + { + } + + /// + /// Initializes a new instance of the class. + /// + public SequenceEnd() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return "Sequence end"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/Events/SequenceStart.cs b/YamlDotNet/Core/Events/SequenceStart.cs index 63a720bb2..93808c643 100644 --- a/YamlDotNet/Core/Events/SequenceStart.cs +++ b/YamlDotNet/Core/Events/SequenceStart.cs @@ -1,105 +1,105 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents a sequence start event. - /// - public sealed class SequenceStart : NodeEvent - { - /// - /// Gets a value indicating the variation of depth caused by this event. - /// The value can be either -1, 0 or 1. For start events, it will be 1, - /// for end events, it will be -1, and for the remaining events, it will be 0. - /// - public override int NestingIncrease => 1; - - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type => EventType.SequenceStart; - - /// - /// Gets a value indicating whether this instance is implicit. - /// - /// - /// true if this instance is implicit; otherwise, false. - /// - public bool IsImplicit { get; } - - /// - /// Gets a value indicating whether this instance is canonical. - /// - /// - public override bool IsCanonical => !IsImplicit; - - /// - /// Gets the style. - /// - /// The style. - public SequenceStyle Style { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The anchor. - /// The tag. - /// if set to true [is implicit]. - /// The style. - /// The start position of the event. - /// The end position of the event. - public SequenceStart(AnchorName anchor, TagName tag, bool isImplicit, SequenceStyle style, Mark start, Mark end) - : base(anchor, tag, start, end) - { - this.IsImplicit = isImplicit; - this.Style = style; - } - - /// - /// Initializes a new instance of the class. - /// - public SequenceStart(AnchorName anchor, TagName tag, bool isImplicit, SequenceStyle style) - : this(anchor, tag, isImplicit, style, Mark.Empty, Mark.Empty) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return $"Sequence start [anchor = {Anchor}, tag = {Tag}, isImplicit = {IsImplicit}, style = {Style}]"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents a sequence start event. + /// + public sealed class SequenceStart : NodeEvent + { + /// + /// Gets a value indicating the variation of depth caused by this event. + /// The value can be either -1, 0 or 1. For start events, it will be 1, + /// for end events, it will be -1, and for the remaining events, it will be 0. + /// + public override int NestingIncrease => 1; + + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type => EventType.SequenceStart; + + /// + /// Gets a value indicating whether this instance is implicit. + /// + /// + /// true if this instance is implicit; otherwise, false. + /// + public bool IsImplicit { get; } + + /// + /// Gets a value indicating whether this instance is canonical. + /// + /// + public override bool IsCanonical => !IsImplicit; + + /// + /// Gets the style. + /// + /// The style. + public SequenceStyle Style { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The anchor. + /// The tag. + /// if set to true [is implicit]. + /// The style. + /// The start position of the event. + /// The end position of the event. + public SequenceStart(AnchorName anchor, TagName tag, bool isImplicit, SequenceStyle style, Mark start, Mark end) + : base(anchor, tag, start, end) + { + this.IsImplicit = isImplicit; + this.Style = style; + } + + /// + /// Initializes a new instance of the class. + /// + public SequenceStart(AnchorName anchor, TagName tag, bool isImplicit, SequenceStyle style) + : this(anchor, tag, isImplicit, style, Mark.Empty, Mark.Empty) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return $"Sequence start [anchor = {Anchor}, tag = {Tag}, isImplicit = {IsImplicit}, style = {Style}]"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/Events/SequenceStyle.cs b/YamlDotNet/Core/Events/SequenceStyle.cs index a36f9f1b5..702147cda 100644 --- a/YamlDotNet/Core/Events/SequenceStyle.cs +++ b/YamlDotNet/Core/Events/SequenceStyle.cs @@ -1,44 +1,44 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Specifies the style of a sequence. - /// - public enum SequenceStyle - { - /// - /// Let the emitter choose the style. - /// - Any, - - /// - /// The block sequence style. - /// - Block, - - /// - /// The flow sequence style. - /// - Flow - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Specifies the style of a sequence. + /// + public enum SequenceStyle + { + /// + /// Let the emitter choose the style. + /// + Any, + + /// + /// The block sequence style. + /// + Block, + + /// + /// The flow sequence style. + /// + Flow + } +} diff --git a/YamlDotNet/Core/Events/StreamEnd.cs b/YamlDotNet/Core/Events/StreamEnd.cs index 296da6fef..c4d492116 100644 --- a/YamlDotNet/Core/Events/StreamEnd.cs +++ b/YamlDotNet/Core/Events/StreamEnd.cs @@ -1,91 +1,91 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents a stream end event. - /// - public sealed class StreamEnd : ParsingEvent - { - /// - /// Gets a value indicating the variation of depth caused by this event. - /// The value can be either -1, 0 or 1. For start events, it will be 1, - /// for end events, it will be -1, and for the remaining events, it will be 0. - /// - public override int NestingIncrease - { - get - { - return -1; - } - } - - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type - { - get - { - return EventType.StreamEnd; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the event. - /// The end position of the event. - public StreamEnd(in Mark start, in Mark end) - : base(start, end) - { - } - - /// - /// Initializes a new instance of the class. - /// - public StreamEnd() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return "Stream end"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents a stream end event. + /// + public sealed class StreamEnd : ParsingEvent + { + /// + /// Gets a value indicating the variation of depth caused by this event. + /// The value can be either -1, 0 or 1. For start events, it will be 1, + /// for end events, it will be -1, and for the remaining events, it will be 0. + /// + public override int NestingIncrease + { + get + { + return -1; + } + } + + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type + { + get + { + return EventType.StreamEnd; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the event. + /// The end position of the event. + public StreamEnd(in Mark start, in Mark end) + : base(start, end) + { + } + + /// + /// Initializes a new instance of the class. + /// + public StreamEnd() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return "Stream end"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/Events/StreamStart.cs b/YamlDotNet/Core/Events/StreamStart.cs index e2f7c7b76..ac9f14214 100644 --- a/YamlDotNet/Core/Events/StreamStart.cs +++ b/YamlDotNet/Core/Events/StreamStart.cs @@ -1,91 +1,91 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Events -{ - /// - /// Represents a stream start event. - /// - public sealed class StreamStart : ParsingEvent - { - /// - /// Gets a value indicating the variation of depth caused by this event. - /// The value can be either -1, 0 or 1. For start events, it will be 1, - /// for end events, it will be -1, and for the remaining events, it will be 0. - /// - public override int NestingIncrease - { - get - { - return 1; - } - } - - /// - /// Gets the event type, which allows for simpler type comparisons. - /// - internal override EventType Type - { - get - { - return EventType.StreamStart; - } - } - - /// - /// Initializes a new instance of the class. - /// - public StreamStart() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the event. - /// The end position of the event. - public StreamStart(in Mark start, in Mark end) - : base(start, end) - { - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - return "Stream start"; - } - - /// - /// Invokes run-time type specific Visit() method of the specified visitor. - /// - /// visitor, may not be null. - public override void Accept(IParsingEventVisitor visitor) - { - visitor.Visit(this); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Events +{ + /// + /// Represents a stream start event. + /// + public sealed class StreamStart : ParsingEvent + { + /// + /// Gets a value indicating the variation of depth caused by this event. + /// The value can be either -1, 0 or 1. For start events, it will be 1, + /// for end events, it will be -1, and for the remaining events, it will be 0. + /// + public override int NestingIncrease + { + get + { + return 1; + } + } + + /// + /// Gets the event type, which allows for simpler type comparisons. + /// + internal override EventType Type + { + get + { + return EventType.StreamStart; + } + } + + /// + /// Initializes a new instance of the class. + /// + public StreamStart() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the event. + /// The end position of the event. + public StreamStart(in Mark start, in Mark end) + : base(start, end) + { + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return "Stream start"; + } + + /// + /// Invokes run-time type specific Visit() method of the specified visitor. + /// + /// visitor, may not be null. + public override void Accept(IParsingEventVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/YamlDotNet/Core/ForwardAnchorNotSupportedException.cs b/YamlDotNet/Core/ForwardAnchorNotSupportedException.cs index ed1078317..bcedb3557 100644 --- a/YamlDotNet/Core/ForwardAnchorNotSupportedException.cs +++ b/YamlDotNet/Core/ForwardAnchorNotSupportedException.cs @@ -1,59 +1,59 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - /// - /// The exception that is thrown when an alias references an anchor - /// that has not yet been defined in a context that does not support forward references. - /// - public sealed class ForwardAnchorNotSupportedException : YamlException - { - /// - /// Initializes a new instance of the class. - /// - /// The message. - public ForwardAnchorNotSupportedException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class. - /// - public ForwardAnchorNotSupportedException(in Mark start, in Mark end, string message) - : base(start, end, message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner. - public ForwardAnchorNotSupportedException(string message, Exception inner) - : base(message, inner) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + /// + /// The exception that is thrown when an alias references an anchor + /// that has not yet been defined in a context that does not support forward references. + /// + public sealed class ForwardAnchorNotSupportedException : YamlException + { + /// + /// Initializes a new instance of the class. + /// + /// The message. + public ForwardAnchorNotSupportedException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + public ForwardAnchorNotSupportedException(in Mark start, in Mark end, string message) + : base(start, end, message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner. + public ForwardAnchorNotSupportedException(string message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/YamlDotNet/Core/HashCode.cs b/YamlDotNet/Core/HashCode.cs index fe80d8b31..765c3bb35 100644 --- a/YamlDotNet/Core/HashCode.cs +++ b/YamlDotNet/Core/HashCode.cs @@ -1,50 +1,50 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core -{ - /// - /// Supports implementations of by providing methods to combine two hash codes. - /// - internal static class HashCode - { - /// - /// Combines two hash codes. - /// - /// The first hash code. - /// The second hash code. - /// - public static int CombineHashCodes(int h1, int h2) - { - return ((h1 << 5) + h1) ^ h2; - } - - public static int CombineHashCodes(int h1, object? o2) - { - return CombineHashCodes(h1, GetHashCode(o2)); - } - - private static int GetHashCode(object? obj) - { - return obj != null ? obj.GetHashCode() : 0; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core +{ + /// + /// Supports implementations of by providing methods to combine two hash codes. + /// + internal static class HashCode + { + /// + /// Combines two hash codes. + /// + /// The first hash code. + /// The second hash code. + /// + public static int CombineHashCodes(int h1, int h2) + { + return ((h1 << 5) + h1) ^ h2; + } + + public static int CombineHashCodes(int h1, object? o2) + { + return CombineHashCodes(h1, GetHashCode(o2)); + } + + private static int GetHashCode(object? obj) + { + return obj != null ? obj.GetHashCode() : 0; + } + } +} diff --git a/YamlDotNet/Core/IEmitter.cs b/YamlDotNet/Core/IEmitter.cs index eae62d26e..c51a06fd6 100644 --- a/YamlDotNet/Core/IEmitter.cs +++ b/YamlDotNet/Core/IEmitter.cs @@ -1,36 +1,36 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Core -{ - /// - /// Represents a YAML stream emitter. - /// - public interface IEmitter - { - /// - /// Emits an event. - /// - void Emit(ParsingEvent @event); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Core +{ + /// + /// Represents a YAML stream emitter. + /// + public interface IEmitter + { + /// + /// Emits an event. + /// + void Emit(ParsingEvent @event); + } +} diff --git a/YamlDotNet/Core/ILookAheadBuffer.cs b/YamlDotNet/Core/ILookAheadBuffer.cs index c92acbef6..717f1ccdf 100644 --- a/YamlDotNet/Core/ILookAheadBuffer.cs +++ b/YamlDotNet/Core/ILookAheadBuffer.cs @@ -1,42 +1,42 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core -{ - internal interface ILookAheadBuffer - { - /// - /// Gets a value indicating whether the end of the input reader has been reached. - /// - bool EndOfInput { get; } - - /// - /// Gets the character at the specified offset. - /// - char Peek(int offset); - - /// - /// Skips the next characters. Those characters must have been - /// obtained first by calling the method. - /// - void Skip(int length); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core +{ + internal interface ILookAheadBuffer + { + /// + /// Gets a value indicating whether the end of the input reader has been reached. + /// + bool EndOfInput { get; } + + /// + /// Gets the character at the specified offset. + /// + char Peek(int offset); + + /// + /// Skips the next characters. Those characters must have been + /// obtained first by calling the method. + /// + void Skip(int length); + } +} diff --git a/YamlDotNet/Core/IParser.cs b/YamlDotNet/Core/IParser.cs index 5ca178ffd..84c53ebac 100644 --- a/YamlDotNet/Core/IParser.cs +++ b/YamlDotNet/Core/IParser.cs @@ -1,43 +1,43 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Core -{ - /// - /// Represents a YAML stream parser. - /// - public interface IParser - { - /// - /// Gets the current event. Returns null before the first call to , - /// and also after returns false. - /// - ParsingEvent? Current { get; } - - /// - /// Moves to the next event. - /// - /// Returns true if there are more events available, otherwise returns false. - bool MoveNext(); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Core +{ + /// + /// Represents a YAML stream parser. + /// + public interface IParser + { + /// + /// Gets the current event. Returns null before the first call to , + /// and also after returns false. + /// + ParsingEvent? Current { get; } + + /// + /// Moves to the next event. + /// + /// Returns true if there are more events available, otherwise returns false. + bool MoveNext(); + } +} diff --git a/YamlDotNet/Core/IScanner.cs b/YamlDotNet/Core/IScanner.cs index 67ceb5428..8636f477c 100644 --- a/YamlDotNet/Core/IScanner.cs +++ b/YamlDotNet/Core/IScanner.cs @@ -1,58 +1,58 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core.Tokens; - -namespace YamlDotNet.Core -{ - /// - /// Defines the interface for a stand-alone YAML scanner that - /// converts a sequence of characters into a sequence of YAML tokens. - /// - public interface IScanner - { - /// - /// Gets the current position inside the input stream. - /// - /// The current position. - Mark CurrentPosition { get; } - - /// - /// Gets the current token. - /// - Token? Current { get; } - - /// - /// Moves to the next token and consumes the current token. - /// - bool MoveNext(); - - /// - /// Moves to the next token without consuming the current token. - /// - bool MoveNextWithoutConsuming(); - - /// - /// Consumes the current token. - /// - void ConsumeCurrent(); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core.Tokens; + +namespace YamlDotNet.Core +{ + /// + /// Defines the interface for a stand-alone YAML scanner that + /// converts a sequence of characters into a sequence of YAML tokens. + /// + public interface IScanner + { + /// + /// Gets the current position inside the input stream. + /// + /// The current position. + Mark CurrentPosition { get; } + + /// + /// Gets the current token. + /// + Token? Current { get; } + + /// + /// Moves to the next token and consumes the current token. + /// + bool MoveNext(); + + /// + /// Moves to the next token without consuming the current token. + /// + bool MoveNextWithoutConsuming(); + + /// + /// Consumes the current token. + /// + void ConsumeCurrent(); + } +} diff --git a/YamlDotNet/Core/InsertionQueue.cs b/YamlDotNet/Core/InsertionQueue.cs index 8ad83438d..2e1b09707 100644 --- a/YamlDotNet/Core/InsertionQueue.cs +++ b/YamlDotNet/Core/InsertionQueue.cs @@ -1,216 +1,216 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using YamlDotNet.Helpers; - -namespace YamlDotNet.Core -{ - /// - /// Generic queue on which items may be inserted - /// - public sealed class InsertionQueue : IEnumerable - { - private const int DefaultInitialCapacity = 1 << 7; // Must be a power of 2 - - // Circular buffer - private T[] items; - private int readPtr; - private int writePtr; - private int mask; - private int count = 0; - - public InsertionQueue(int initialCapacity = DefaultInitialCapacity) - { - if (initialCapacity <= 0) - { - throw new ArgumentOutOfRangeException(nameof(initialCapacity), "The initial capacity must be a positive number."); - } - - if (!initialCapacity.IsPowerOfTwo()) - { - throw new ArgumentException("The initial capacity must be a power of 2.", nameof(initialCapacity)); - } - - items = new T[initialCapacity]; - readPtr = initialCapacity / 2; - writePtr = initialCapacity / 2; - mask = initialCapacity - 1; - } - - /// - /// Gets the number of items that are contained by the queue. - /// - public int Count => count; - public int Capacity => items.Length; - - /// - /// Enqueues the specified item. - /// - /// The item to be enqueued. - public void Enqueue(T item) - { - ResizeIfNeeded(); - - items[writePtr] = item; - writePtr = (writePtr - 1) & mask; - ++count; - } - - /// - /// Dequeues an item. - /// - /// Returns the item that been dequeued. - public T Dequeue() - { - if (count == 0) - { - throw new InvalidOperationException("The queue is empty"); - } - - var item = items[readPtr]; - readPtr = (readPtr - 1) & mask; - --count; - return item; - } - - /// - /// Inserts an item at the specified index. - /// - /// The index where to insert the item. - /// The item to be inserted. - public void Insert(int index, T item) - { - if (index > count) - { - throw new InvalidOperationException("Cannot insert outside of the bounds of the queue"); - } - - ResizeIfNeeded(); - - CalculateInsertionParameters( - mask, count, index, - ref readPtr, ref writePtr, - out var insertPtr, - out var copyIndex, out var copyOffset, out var copyLength - ); - - if (copyLength != 0) - { - Array.Copy(items, copyIndex, items, copyIndex + copyOffset, copyLength); - } - - items[insertPtr] = item; - ++count; - } - - private void ResizeIfNeeded() - { - var capacity = items.Length; - if (count == capacity) - { - Debug.Assert(readPtr == writePtr); - - var newItems = new T[capacity * 2]; - - var beginCount = readPtr + 1; - if (beginCount > 0) - { - Array.Copy(items, 0, newItems, 0, beginCount); - } - - writePtr += capacity; - var endCount = capacity - beginCount; - if (endCount > 0) - { - Array.Copy(items, readPtr + 1, newItems, writePtr + 1, endCount); - } - - items = newItems; - mask = mask * 2 + 1; - } - } - - internal static void CalculateInsertionParameters(int mask, int count, int index, ref int readPtr, ref int writePtr, out int insertPtr, out int copyIndex, out int copyOffset, out int copyLength) - { - var indexOfLastElement = (readPtr + 1) & mask; - if (index == 0) - { - insertPtr = readPtr = indexOfLastElement; - - // No copy is needed - copyIndex = 0; - copyOffset = 0; - copyLength = 0; - return; - } - - insertPtr = (readPtr - index) & mask; - if (index == count) - { - writePtr = (writePtr - 1) & mask; - - // No copy is needed - copyIndex = 0; - copyOffset = 0; - copyLength = 0; - return; - } - - var canMoveRight = indexOfLastElement >= insertPtr; - var moveRightCost = canMoveRight ? readPtr - insertPtr : int.MaxValue; - - var canMoveLeft = writePtr <= insertPtr; - var moveLeftCost = canMoveLeft ? insertPtr - writePtr : int.MaxValue; - - if (moveRightCost <= moveLeftCost) - { - ++insertPtr; - ++readPtr; - copyIndex = insertPtr; - copyOffset = 1; - copyLength = moveRightCost; - } - else - { - copyIndex = writePtr + 1; - copyOffset = -1; - copyLength = moveLeftCost; - writePtr = (writePtr - 1) & mask; - } - } - - public IEnumerator GetEnumerator() - { - var ptr = readPtr; - for (var i = 0; i < Count; i++) - { - yield return items[ptr]; - ptr = (ptr - 1) & mask; - } - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using YamlDotNet.Helpers; + +namespace YamlDotNet.Core +{ + /// + /// Generic queue on which items may be inserted + /// + public sealed class InsertionQueue : IEnumerable + { + private const int DefaultInitialCapacity = 1 << 7; // Must be a power of 2 + + // Circular buffer + private T[] items; + private int readPtr; + private int writePtr; + private int mask; + private int count = 0; + + public InsertionQueue(int initialCapacity = DefaultInitialCapacity) + { + if (initialCapacity <= 0) + { + throw new ArgumentOutOfRangeException(nameof(initialCapacity), "The initial capacity must be a positive number."); + } + + if (!initialCapacity.IsPowerOfTwo()) + { + throw new ArgumentException("The initial capacity must be a power of 2.", nameof(initialCapacity)); + } + + items = new T[initialCapacity]; + readPtr = initialCapacity / 2; + writePtr = initialCapacity / 2; + mask = initialCapacity - 1; + } + + /// + /// Gets the number of items that are contained by the queue. + /// + public int Count => count; + public int Capacity => items.Length; + + /// + /// Enqueues the specified item. + /// + /// The item to be enqueued. + public void Enqueue(T item) + { + ResizeIfNeeded(); + + items[writePtr] = item; + writePtr = (writePtr - 1) & mask; + ++count; + } + + /// + /// Dequeues an item. + /// + /// Returns the item that been dequeued. + public T Dequeue() + { + if (count == 0) + { + throw new InvalidOperationException("The queue is empty"); + } + + var item = items[readPtr]; + readPtr = (readPtr - 1) & mask; + --count; + return item; + } + + /// + /// Inserts an item at the specified index. + /// + /// The index where to insert the item. + /// The item to be inserted. + public void Insert(int index, T item) + { + if (index > count) + { + throw new InvalidOperationException("Cannot insert outside of the bounds of the queue"); + } + + ResizeIfNeeded(); + + CalculateInsertionParameters( + mask, count, index, + ref readPtr, ref writePtr, + out var insertPtr, + out var copyIndex, out var copyOffset, out var copyLength + ); + + if (copyLength != 0) + { + Array.Copy(items, copyIndex, items, copyIndex + copyOffset, copyLength); + } + + items[insertPtr] = item; + ++count; + } + + private void ResizeIfNeeded() + { + var capacity = items.Length; + if (count == capacity) + { + Debug.Assert(readPtr == writePtr); + + var newItems = new T[capacity * 2]; + + var beginCount = readPtr + 1; + if (beginCount > 0) + { + Array.Copy(items, 0, newItems, 0, beginCount); + } + + writePtr += capacity; + var endCount = capacity - beginCount; + if (endCount > 0) + { + Array.Copy(items, readPtr + 1, newItems, writePtr + 1, endCount); + } + + items = newItems; + mask = mask * 2 + 1; + } + } + + internal static void CalculateInsertionParameters(int mask, int count, int index, ref int readPtr, ref int writePtr, out int insertPtr, out int copyIndex, out int copyOffset, out int copyLength) + { + var indexOfLastElement = (readPtr + 1) & mask; + if (index == 0) + { + insertPtr = readPtr = indexOfLastElement; + + // No copy is needed + copyIndex = 0; + copyOffset = 0; + copyLength = 0; + return; + } + + insertPtr = (readPtr - index) & mask; + if (index == count) + { + writePtr = (writePtr - 1) & mask; + + // No copy is needed + copyIndex = 0; + copyOffset = 0; + copyLength = 0; + return; + } + + var canMoveRight = indexOfLastElement >= insertPtr; + var moveRightCost = canMoveRight ? readPtr - insertPtr : int.MaxValue; + + var canMoveLeft = writePtr <= insertPtr; + var moveLeftCost = canMoveLeft ? insertPtr - writePtr : int.MaxValue; + + if (moveRightCost <= moveLeftCost) + { + ++insertPtr; + ++readPtr; + copyIndex = insertPtr; + copyOffset = 1; + copyLength = moveRightCost; + } + else + { + copyIndex = writePtr + 1; + copyOffset = -1; + copyLength = moveLeftCost; + writePtr = (writePtr - 1) & mask; + } + } + + public IEnumerator GetEnumerator() + { + var ptr = readPtr; + for (var i = 0; i < Count; i++) + { + yield return items[ptr]; + ptr = (ptr - 1) & mask; + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/YamlDotNet/Core/LookAheadBuffer.cs b/YamlDotNet/Core/LookAheadBuffer.cs index 36f64a4c7..85f8dc5a5 100644 --- a/YamlDotNet/Core/LookAheadBuffer.cs +++ b/YamlDotNet/Core/LookAheadBuffer.cs @@ -1,171 +1,171 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Diagnostics; -using System.IO; -using YamlDotNet.Helpers; - -namespace YamlDotNet.Core -{ - /// - /// Provides access to a stream and allows to peek at the next characters, - /// up to the buffer's capacity. - /// - /// - /// This class implements a circular buffer with a fixed capacity. - /// - [DebuggerStepThrough] - public sealed class LookAheadBuffer : ILookAheadBuffer - { - private readonly TextReader input; - private readonly char[] buffer; - private readonly int blockSize; - private readonly int mask; - private int firstIndex; - private int writeOffset; - private int count; - private bool endOfInput; - - /// - /// Initializes a new instance of the class. - /// - /// The input. - /// The capacity. - public LookAheadBuffer(TextReader input, int capacity) - { - if (capacity < 1) - { - throw new ArgumentOutOfRangeException(nameof(capacity), "The capacity must be positive."); - } - - if (!capacity.IsPowerOfTwo()) - { - throw new ArgumentException("The capacity must be a power of 2.", nameof(capacity)); - } - - this.input = input ?? throw new ArgumentNullException(nameof(input)); - - blockSize = capacity; - - // Allocate twice the required capacity to ensure that - buffer = new char[capacity * 2]; - mask = capacity * 2 - 1; - } - - /// - /// Gets a value indicating whether the end of the input reader has been reached. - /// - public bool EndOfInput => endOfInput && count == 0; - - /// - /// Gets the index of the character for the specified offset. - /// - private int GetIndexForOffset(int offset) - { - return (firstIndex + offset) & mask; - } - - /// - /// Gets the character at the specified offset. - /// - public char Peek(int offset) - { -#if DEBUG - if (offset < 0 || offset >= blockSize) - { - throw new ArgumentOutOfRangeException(nameof(offset), "The offset must be between zero and the capacity of the buffer."); - } -#endif - if (offset >= count) - { - FillBuffer(); - } - - if (offset < count) - { - return buffer[(firstIndex + offset) & mask]; - } - else - { - return '\0'; - } - } - - /// - /// Reads characters until at least characters are in the buffer. - /// - /// - /// Number of characters to cache. - /// - public void Cache(int length) - { - if (length >= count) - { - FillBuffer(); - } - } - - private void FillBuffer() - { - if (endOfInput) - { - return; - } - - var remainingSize = blockSize; - do - { - var readCount = input.Read(buffer, writeOffset, remainingSize); - if (readCount == 0) - { - endOfInput = true; - return; - } - - remainingSize -= readCount; - writeOffset += readCount; - count += readCount; - } while (remainingSize > 0); - - if (writeOffset == buffer.Length) - { - writeOffset = 0; - } - - Debug.Assert(writeOffset == 0 || writeOffset == blockSize); - } - - /// - /// Skips the next characters. Those characters must have been - /// obtained first by calling the or methods. - /// - public void Skip(int length) - { - if (length < 1 || length > blockSize) - { - throw new ArgumentOutOfRangeException(nameof(length), "The length must be between 1 and the number of characters in the buffer. Use the Peek() and / or Cache() methods to fill the buffer."); - } - firstIndex = GetIndexForOffset(length); - count -= length; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Diagnostics; +using System.IO; +using YamlDotNet.Helpers; + +namespace YamlDotNet.Core +{ + /// + /// Provides access to a stream and allows to peek at the next characters, + /// up to the buffer's capacity. + /// + /// + /// This class implements a circular buffer with a fixed capacity. + /// + [DebuggerStepThrough] + public sealed class LookAheadBuffer : ILookAheadBuffer + { + private readonly TextReader input; + private readonly char[] buffer; + private readonly int blockSize; + private readonly int mask; + private int firstIndex; + private int writeOffset; + private int count; + private bool endOfInput; + + /// + /// Initializes a new instance of the class. + /// + /// The input. + /// The capacity. + public LookAheadBuffer(TextReader input, int capacity) + { + if (capacity < 1) + { + throw new ArgumentOutOfRangeException(nameof(capacity), "The capacity must be positive."); + } + + if (!capacity.IsPowerOfTwo()) + { + throw new ArgumentException("The capacity must be a power of 2.", nameof(capacity)); + } + + this.input = input ?? throw new ArgumentNullException(nameof(input)); + + blockSize = capacity; + + // Allocate twice the required capacity to ensure that + buffer = new char[capacity * 2]; + mask = capacity * 2 - 1; + } + + /// + /// Gets a value indicating whether the end of the input reader has been reached. + /// + public bool EndOfInput => endOfInput && count == 0; + + /// + /// Gets the index of the character for the specified offset. + /// + private int GetIndexForOffset(int offset) + { + return (firstIndex + offset) & mask; + } + + /// + /// Gets the character at the specified offset. + /// + public char Peek(int offset) + { +#if DEBUG + if (offset < 0 || offset >= blockSize) + { + throw new ArgumentOutOfRangeException(nameof(offset), "The offset must be between zero and the capacity of the buffer."); + } +#endif + if (offset >= count) + { + FillBuffer(); + } + + if (offset < count) + { + return buffer[(firstIndex + offset) & mask]; + } + else + { + return '\0'; + } + } + + /// + /// Reads characters until at least characters are in the buffer. + /// + /// + /// Number of characters to cache. + /// + public void Cache(int length) + { + if (length >= count) + { + FillBuffer(); + } + } + + private void FillBuffer() + { + if (endOfInput) + { + return; + } + + var remainingSize = blockSize; + do + { + var readCount = input.Read(buffer, writeOffset, remainingSize); + if (readCount == 0) + { + endOfInput = true; + return; + } + + remainingSize -= readCount; + writeOffset += readCount; + count += readCount; + } while (remainingSize > 0); + + if (writeOffset == buffer.Length) + { + writeOffset = 0; + } + + Debug.Assert(writeOffset == 0 || writeOffset == blockSize); + } + + /// + /// Skips the next characters. Those characters must have been + /// obtained first by calling the or methods. + /// + public void Skip(int length) + { + if (length < 1 || length > blockSize) + { + throw new ArgumentOutOfRangeException(nameof(length), "The length must be between 1 and the number of characters in the buffer. Use the Peek() and / or Cache() methods to fill the buffer."); + } + firstIndex = GetIndexForOffset(length); + count -= length; + } + } +} diff --git a/YamlDotNet/Core/Mark.cs b/YamlDotNet/Core/Mark.cs index e9b6a61fd..874924393 100644 --- a/YamlDotNet/Core/Mark.cs +++ b/YamlDotNet/Core/Mark.cs @@ -1,126 +1,126 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Helpers; - -namespace YamlDotNet.Core -{ - /// - /// Represents a location inside a file - /// - public readonly struct Mark : IEquatable, IComparable, IComparable - { - /// - /// Gets a with empty values. - /// - public static readonly Mark Empty = new Mark(0, 1, 1); - - /// - /// Gets / sets the absolute offset in the file - /// - public int Index { get; } - - /// - /// Gets / sets the number of the line - /// - public int Line { get; } - - /// - /// Gets / sets the index of the column - /// - public int Column { get; } - - public Mark(int index, int line, int column) - { - if (index < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeException(nameof(index), "Index must be greater than or equal to zero."); - } - if (line < 1) - { - ThrowHelper.ThrowArgumentOutOfRangeException(nameof(line), "Line must be greater than or equal to 1."); - } - if (column < 1) - { - ThrowHelper.ThrowArgumentOutOfRangeException(nameof(column), "Column must be greater than or equal to 1."); - } - - Index = index; - Line = line; - Column = column; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"Line: {Line}, Col: {Column}, Idx: {Index}"; - } - - /// - public override bool Equals(object? obj) - { - return Equals((Mark)(obj ?? Empty)); - } - - /// - public bool Equals(Mark other) - { - return Index == other.Index - && Line == other.Line - && Column == other.Column; - } - - /// - public override int GetHashCode() - { - return HashCode.CombineHashCodes( - Index.GetHashCode(), - HashCode.CombineHashCodes( - Line.GetHashCode(), - Column.GetHashCode() - ) - ); - } - - /// - public int CompareTo(object? obj) - { - return CompareTo((Mark)(obj ?? Empty)); - } - - /// - public int CompareTo(Mark other) - { - var cmp = Line.CompareTo(other.Line); - if (cmp == 0) - { - cmp = Column.CompareTo(other.Column); - } - return cmp; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Helpers; + +namespace YamlDotNet.Core +{ + /// + /// Represents a location inside a file + /// + public readonly struct Mark : IEquatable, IComparable, IComparable + { + /// + /// Gets a with empty values. + /// + public static readonly Mark Empty = new Mark(0, 1, 1); + + /// + /// Gets / sets the absolute offset in the file + /// + public int Index { get; } + + /// + /// Gets / sets the number of the line + /// + public int Line { get; } + + /// + /// Gets / sets the index of the column + /// + public int Column { get; } + + public Mark(int index, int line, int column) + { + if (index < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(index), "Index must be greater than or equal to zero."); + } + if (line < 1) + { + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(line), "Line must be greater than or equal to 1."); + } + if (column < 1) + { + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(column), "Column must be greater than or equal to 1."); + } + + Index = index; + Line = line; + Column = column; + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return $"Line: {Line}, Col: {Column}, Idx: {Index}"; + } + + /// + public override bool Equals(object? obj) + { + return Equals((Mark)(obj ?? Empty)); + } + + /// + public bool Equals(Mark other) + { + return Index == other.Index + && Line == other.Line + && Column == other.Column; + } + + /// + public override int GetHashCode() + { + return HashCode.CombineHashCodes( + Index.GetHashCode(), + HashCode.CombineHashCodes( + Line.GetHashCode(), + Column.GetHashCode() + ) + ); + } + + /// + public int CompareTo(object? obj) + { + return CompareTo((Mark)(obj ?? Empty)); + } + + /// + public int CompareTo(Mark other) + { + var cmp = Line.CompareTo(other.Line); + if (cmp == 0) + { + cmp = Column.CompareTo(other.Column); + } + return cmp; + } + } +} diff --git a/YamlDotNet/Core/MaximumRecursionLevelReachedException.cs b/YamlDotNet/Core/MaximumRecursionLevelReachedException.cs index 7287b8dfa..d334ca740 100644 --- a/YamlDotNet/Core/MaximumRecursionLevelReachedException.cs +++ b/YamlDotNet/Core/MaximumRecursionLevelReachedException.cs @@ -1,58 +1,58 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - /// - /// Exception that is thrown when an infinite recursion is detected. - /// - public sealed class MaximumRecursionLevelReachedException : YamlException - { - /// - /// Initializes a new instance of the class. - /// - /// The message. - public MaximumRecursionLevelReachedException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class. - /// - public MaximumRecursionLevelReachedException(in Mark start, in Mark end, string message) - : base(start, end, message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner. - public MaximumRecursionLevelReachedException(string message, Exception inner) - : base(message, inner) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + /// + /// Exception that is thrown when an infinite recursion is detected. + /// + public sealed class MaximumRecursionLevelReachedException : YamlException + { + /// + /// Initializes a new instance of the class. + /// + /// The message. + public MaximumRecursionLevelReachedException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + public MaximumRecursionLevelReachedException(in Mark start, in Mark end, string message) + : base(start, end, message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner. + public MaximumRecursionLevelReachedException(string message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/YamlDotNet/Core/MergingParser.cs b/YamlDotNet/Core/MergingParser.cs index b4fbcdea8..2bde3d308 100644 --- a/YamlDotNet/Core/MergingParser.cs +++ b/YamlDotNet/Core/MergingParser.cs @@ -1,311 +1,311 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Core -{ - /// - /// Simple implementation of that implements merging: http://yaml.org/type/merge.html - /// - public sealed class MergingParser : IParser - { - private readonly ParsingEventCollection events; - private readonly IParser innerParser; - private IEnumerator> iterator; - private bool merged; - - public MergingParser(IParser innerParser) - { - events = new ParsingEventCollection(); - merged = false; - iterator = events.GetEnumerator(); - this.innerParser = innerParser; - } - - public ParsingEvent? Current => iterator.Current?.Value; - - public bool MoveNext() - { - if (!merged) - { - Merge(); - events.CleanMarked(); - iterator = events.GetEnumerator(); - merged = true; - } - - return iterator.MoveNext(); - } - - private void Merge() - { - while (innerParser.MoveNext()) - { - events.Add(innerParser.Current!); - } - - foreach (var node in events) - { - if (IsMergeToken(node)) - { - events.MarkDeleted(node); - if (!HandleMerge(node.Next)) - { - throw new SemanticErrorException(node.Value.Start, node.Value.End, "Unrecognized merge key pattern"); - } - } - } - } - - private bool HandleMerge(LinkedListNode? node) - { - if (node == null) - { - return false; - } - - if (node.Value is AnchorAlias anchorAlias) - { - return HandleAnchorAlias(node, node, anchorAlias); - } - - if (node.Value is SequenceStart) - { - return HandleSequence(node); - } - - return false; - } - - private bool HandleMergeSequence(LinkedListNode sequenceStart, LinkedListNode? node) - { - if (node is null) - { - return false; - } - if (node.Value is AnchorAlias anchorAlias) - { - return HandleAnchorAlias(sequenceStart, node, anchorAlias); - } - if (node.Value is SequenceStart) - { - return HandleSequence(node); - } - return false; - } - - private bool IsMergeToken(LinkedListNode node) - { - return node.Value is Scalar merge && merge.Value == "<<"; - } - - private bool HandleAnchorAlias(LinkedListNode node, LinkedListNode anchorNode, AnchorAlias anchorAlias) - { - var mergedEvents = GetMappingEvents(anchorAlias.Value); - - events.AddAfter(node, mergedEvents); - events.MarkDeleted(anchorNode); - - return true; - } - - private bool HandleSequence(LinkedListNode node) - { - var sequenceStart = node; - events.MarkDeleted(node); - - var current = node; - while (current != null) - { - if (current.Value is SequenceEnd) - { - events.MarkDeleted(current); - return true; - } - - var next = current.Next; - HandleMergeSequence(sequenceStart, next); - current = next; - } - - return true; - } - - private IEnumerable GetMappingEvents(AnchorName anchor) - { - var cloner = new ParsingEventCloner(); - var nesting = 0; - - return events.FromAnchor(anchor) - .Select(e => e.Value) - .TakeWhile(e => (nesting += e.NestingIncrease) >= 0) - .Select(e => cloner.Clone(e)); - } - - private sealed class ParsingEventCollection : IEnumerable> - { - private readonly LinkedList events; - private readonly HashSet> deleted; - private readonly Dictionary> references; - - public ParsingEventCollection() - { - events = new LinkedList(); - deleted = new HashSet>(); - references = new Dictionary>(); - } - - public void AddAfter(LinkedListNode node, IEnumerable items) - { - foreach (var item in items) - { - node = events.AddAfter(node, item); - } - } - - public void Add(ParsingEvent item) - { - var node = events.AddLast(item); - AddReference(item, node); - } - - public void MarkDeleted(LinkedListNode node) - { - deleted.Add(node); - } - - public void CleanMarked() - { - foreach (var node in deleted) - { - events.Remove(node); - } - } - - public IEnumerable> FromAnchor(AnchorName anchor) - { - var node = references[anchor].Next; - return Enumerate(node); - } - - public IEnumerator> GetEnumerator() => Enumerate(events.First).GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - private IEnumerable> Enumerate(LinkedListNode? node) - { - while (node != null) - { - yield return node; - node = node.Next; - } - } - - private void AddReference(ParsingEvent item, LinkedListNode node) - { - if (item is MappingStart mappingStart) - { - var anchor = mappingStart.Anchor; - if (!anchor.IsEmpty) - { - references[anchor] = node; - } - } - } - } - - private sealed class ParsingEventCloner : IParsingEventVisitor - { - private ParsingEvent? clonedEvent; - - public ParsingEvent Clone(ParsingEvent e) - { - e.Accept(this); - if (clonedEvent == null) - { - throw new InvalidOperationException($"Could not clone event of type '{e.Type}'"); - } - - return clonedEvent; - } - - void IParsingEventVisitor.Visit(AnchorAlias e) - { - clonedEvent = new AnchorAlias(e.Value, e.Start, e.End); - } - - void IParsingEventVisitor.Visit(StreamStart e) - { - throw new NotSupportedException(); - } - - void IParsingEventVisitor.Visit(StreamEnd e) - { - throw new NotSupportedException(); - } - - void IParsingEventVisitor.Visit(DocumentStart e) - { - throw new NotSupportedException(); - } - - void IParsingEventVisitor.Visit(DocumentEnd e) - { - throw new NotSupportedException(); - } - - void IParsingEventVisitor.Visit(Scalar e) - { - clonedEvent = new Scalar(AnchorName.Empty, e.Tag, e.Value, e.Style, e.IsPlainImplicit, e.IsQuotedImplicit, e.Start, e.End); - } - - void IParsingEventVisitor.Visit(SequenceStart e) - { - clonedEvent = new SequenceStart(AnchorName.Empty, e.Tag, e.IsImplicit, e.Style, e.Start, e.End); - } - - void IParsingEventVisitor.Visit(SequenceEnd e) - { - clonedEvent = new SequenceEnd(e.Start, e.End); - } - - void IParsingEventVisitor.Visit(MappingStart e) - { - clonedEvent = new MappingStart(AnchorName.Empty, e.Tag, e.IsImplicit, e.Style, e.Start, e.End); - } - - void IParsingEventVisitor.Visit(MappingEnd e) - { - clonedEvent = new MappingEnd(e.Start, e.End); - } - - void IParsingEventVisitor.Visit(Comment e) - { - throw new NotSupportedException(); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Core +{ + /// + /// Simple implementation of that implements merging: http://yaml.org/type/merge.html + /// + public sealed class MergingParser : IParser + { + private readonly ParsingEventCollection events; + private readonly IParser innerParser; + private IEnumerator> iterator; + private bool merged; + + public MergingParser(IParser innerParser) + { + events = new ParsingEventCollection(); + merged = false; + iterator = events.GetEnumerator(); + this.innerParser = innerParser; + } + + public ParsingEvent? Current => iterator.Current?.Value; + + public bool MoveNext() + { + if (!merged) + { + Merge(); + events.CleanMarked(); + iterator = events.GetEnumerator(); + merged = true; + } + + return iterator.MoveNext(); + } + + private void Merge() + { + while (innerParser.MoveNext()) + { + events.Add(innerParser.Current!); + } + + foreach (var node in events) + { + if (IsMergeToken(node)) + { + events.MarkDeleted(node); + if (!HandleMerge(node.Next)) + { + throw new SemanticErrorException(node.Value.Start, node.Value.End, "Unrecognized merge key pattern"); + } + } + } + } + + private bool HandleMerge(LinkedListNode? node) + { + if (node == null) + { + return false; + } + + if (node.Value is AnchorAlias anchorAlias) + { + return HandleAnchorAlias(node, node, anchorAlias); + } + + if (node.Value is SequenceStart) + { + return HandleSequence(node); + } + + return false; + } + + private bool HandleMergeSequence(LinkedListNode sequenceStart, LinkedListNode? node) + { + if (node is null) + { + return false; + } + if (node.Value is AnchorAlias anchorAlias) + { + return HandleAnchorAlias(sequenceStart, node, anchorAlias); + } + if (node.Value is SequenceStart) + { + return HandleSequence(node); + } + return false; + } + + private bool IsMergeToken(LinkedListNode node) + { + return node.Value is Scalar merge && merge.Value == "<<"; + } + + private bool HandleAnchorAlias(LinkedListNode node, LinkedListNode anchorNode, AnchorAlias anchorAlias) + { + var mergedEvents = GetMappingEvents(anchorAlias.Value); + + events.AddAfter(node, mergedEvents); + events.MarkDeleted(anchorNode); + + return true; + } + + private bool HandleSequence(LinkedListNode node) + { + var sequenceStart = node; + events.MarkDeleted(node); + + var current = node; + while (current != null) + { + if (current.Value is SequenceEnd) + { + events.MarkDeleted(current); + return true; + } + + var next = current.Next; + HandleMergeSequence(sequenceStart, next); + current = next; + } + + return true; + } + + private IEnumerable GetMappingEvents(AnchorName anchor) + { + var cloner = new ParsingEventCloner(); + var nesting = 0; + + return events.FromAnchor(anchor) + .Select(e => e.Value) + .TakeWhile(e => (nesting += e.NestingIncrease) >= 0) + .Select(e => cloner.Clone(e)); + } + + private sealed class ParsingEventCollection : IEnumerable> + { + private readonly LinkedList events; + private readonly HashSet> deleted; + private readonly Dictionary> references; + + public ParsingEventCollection() + { + events = new LinkedList(); + deleted = new HashSet>(); + references = new Dictionary>(); + } + + public void AddAfter(LinkedListNode node, IEnumerable items) + { + foreach (var item in items) + { + node = events.AddAfter(node, item); + } + } + + public void Add(ParsingEvent item) + { + var node = events.AddLast(item); + AddReference(item, node); + } + + public void MarkDeleted(LinkedListNode node) + { + deleted.Add(node); + } + + public void CleanMarked() + { + foreach (var node in deleted) + { + events.Remove(node); + } + } + + public IEnumerable> FromAnchor(AnchorName anchor) + { + var node = references[anchor].Next; + return Enumerate(node); + } + + public IEnumerator> GetEnumerator() => Enumerate(events.First).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private IEnumerable> Enumerate(LinkedListNode? node) + { + while (node != null) + { + yield return node; + node = node.Next; + } + } + + private void AddReference(ParsingEvent item, LinkedListNode node) + { + if (item is MappingStart mappingStart) + { + var anchor = mappingStart.Anchor; + if (!anchor.IsEmpty) + { + references[anchor] = node; + } + } + } + } + + private sealed class ParsingEventCloner : IParsingEventVisitor + { + private ParsingEvent? clonedEvent; + + public ParsingEvent Clone(ParsingEvent e) + { + e.Accept(this); + if (clonedEvent == null) + { + throw new InvalidOperationException($"Could not clone event of type '{e.Type}'"); + } + + return clonedEvent; + } + + void IParsingEventVisitor.Visit(AnchorAlias e) + { + clonedEvent = new AnchorAlias(e.Value, e.Start, e.End); + } + + void IParsingEventVisitor.Visit(StreamStart e) + { + throw new NotSupportedException(); + } + + void IParsingEventVisitor.Visit(StreamEnd e) + { + throw new NotSupportedException(); + } + + void IParsingEventVisitor.Visit(DocumentStart e) + { + throw new NotSupportedException(); + } + + void IParsingEventVisitor.Visit(DocumentEnd e) + { + throw new NotSupportedException(); + } + + void IParsingEventVisitor.Visit(Scalar e) + { + clonedEvent = new Scalar(AnchorName.Empty, e.Tag, e.Value, e.Style, e.IsPlainImplicit, e.IsQuotedImplicit, e.Start, e.End); + } + + void IParsingEventVisitor.Visit(SequenceStart e) + { + clonedEvent = new SequenceStart(AnchorName.Empty, e.Tag, e.IsImplicit, e.Style, e.Start, e.End); + } + + void IParsingEventVisitor.Visit(SequenceEnd e) + { + clonedEvent = new SequenceEnd(e.Start, e.End); + } + + void IParsingEventVisitor.Visit(MappingStart e) + { + clonedEvent = new MappingStart(AnchorName.Empty, e.Tag, e.IsImplicit, e.Style, e.Start, e.End); + } + + void IParsingEventVisitor.Visit(MappingEnd e) + { + clonedEvent = new MappingEnd(e.Start, e.End); + } + + void IParsingEventVisitor.Visit(Comment e) + { + throw new NotSupportedException(); + } + } + } +} diff --git a/YamlDotNet/Core/Parser.cs b/YamlDotNet/Core/Parser.cs index ef3d10551..df877ce71 100644 --- a/YamlDotNet/Core/Parser.cs +++ b/YamlDotNet/Core/Parser.cs @@ -1,1104 +1,1104 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using YamlDotNet.Core.Tokens; -using MappingStyle = YamlDotNet.Core.Events.MappingStyle; -using ParsingEvent = YamlDotNet.Core.Events.ParsingEvent; -using SequenceStyle = YamlDotNet.Core.Events.SequenceStyle; - -namespace YamlDotNet.Core -{ - /// - /// Parses YAML streams. - /// - public class Parser : IParser - { - private readonly Stack states = new Stack(); - private readonly TagDirectiveCollection tagDirectives = new TagDirectiveCollection(); - private ParserState state; - - private readonly IScanner scanner; - private Token? currentToken; - private VersionDirective? version; - - private Token? GetCurrentToken() - { - if (currentToken == null) - { - while (scanner.MoveNextWithoutConsuming()) - { - currentToken = scanner.Current; - - if (currentToken is Comment commentToken) - { - pendingEvents.Enqueue(new Events.Comment(commentToken.Value, commentToken.IsInline, commentToken.Start, commentToken.End)); - scanner.ConsumeCurrent(); - } - else - { - break; - } - } - } - return currentToken; - } - - /// - /// Initializes a new instance of the class. - /// - /// The input where the YAML stream is to be read. - public Parser(TextReader input) - : this(new Scanner(input)) - { - } - - /// - /// Initializes a new instance of the class. - /// - public Parser(IScanner scanner) - { - this.scanner = scanner; - } - - /// - /// Gets the current event. - /// - public ParsingEvent? Current { get; private set; } - - private readonly EventQueue pendingEvents = new EventQueue(); - - /// - /// Moves to the next event. - /// - /// Returns true if there are more events available, otherwise returns false. - public bool MoveNext() - { - // No events after the end of the stream or error. - if (state == ParserState.StreamEnd) - { - Current = null; - return false; - } - else if (pendingEvents.Count == 0) - { - // Generate the next event. - pendingEvents.Enqueue(StateMachine()); - } - - Current = pendingEvents.Dequeue(); - return true; - } - - private ParsingEvent StateMachine() - { - switch (state) - { - case ParserState.StreamStart: - return ParseStreamStart(); - - case ParserState.ImplicitDocumentStart: - return ParseDocumentStart(true); - - case ParserState.DocumentStart: - return ParseDocumentStart(false); - - case ParserState.DocumentContent: - return ParseDocumentContent(); - - case ParserState.DocumentEnd: - return ParseDocumentEnd(); - - case ParserState.BlockNode: - return ParseNode(true, false); - - case ParserState.BlockNodeOrIndentlessSequence: - return ParseNode(true, true); - - case ParserState.FlowNode: - return ParseNode(false, false); - - case ParserState.BlockSequenceFirstEntry: - return ParseBlockSequenceEntry(true); - - case ParserState.BlockSequenceEntry: - return ParseBlockSequenceEntry(false); - - case ParserState.IndentlessSequenceEntry: - return ParseIndentlessSequenceEntry(); - - case ParserState.BlockMappingFirstKey: - return ParseBlockMappingKey(true); - - case ParserState.BlockMappingKey: - return ParseBlockMappingKey(false); - - case ParserState.BlockMappingValue: - return ParseBlockMappingValue(); - - case ParserState.FlowSequenceFirstEntry: - return ParseFlowSequenceEntry(true); - - case ParserState.FlowSequenceEntry: - return ParseFlowSequenceEntry(false); - - case ParserState.FlowSequenceEntryMappingKey: - return ParseFlowSequenceEntryMappingKey(); - - case ParserState.FlowSequenceEntryMappingValue: - return ParseFlowSequenceEntryMappingValue(); - - case ParserState.FlowSequenceEntryMappingEnd: - return ParseFlowSequenceEntryMappingEnd(); - - case ParserState.FlowMappingFirstKey: - return ParseFlowMappingKey(true); - - case ParserState.FlowMappingKey: - return ParseFlowMappingKey(false); - - case ParserState.FlowMappingValue: - return ParseFlowMappingValue(false); - - case ParserState.FlowMappingEmptyValue: - return ParseFlowMappingValue(true); - - default: - Debug.Assert(false, "Invalid state"); // Invalid state. - throw new InvalidOperationException(); - } - } - - private void Skip() - { - if (currentToken != null) - { - currentToken = null; - scanner.ConsumeCurrent(); - } - } - - /// - /// Parse the production: - /// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END - /// ************ - /// - private ParsingEvent ParseStreamStart() - { - var current = GetCurrentToken(); - - if (!(current is StreamStart streamStart)) - { - throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "Did not find expected ."); - } - Skip(); - - state = ParserState.ImplicitDocumentStart; - return new Events.StreamStart(streamStart.Start, streamStart.End); - } - - /// - /// Parse the productions: - /// implicit_document ::= block_node DOCUMENT-END* - /// * - /// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* - /// ************************* - /// - private ParsingEvent ParseDocumentStart(bool isImplicit) - { - if (currentToken is VersionDirective) - { - // EB22 - throw new SyntaxErrorException("While parsing a document start node, could not find document end marker before version directive."); - } - - // Parse extra document end indicators. - - var current = GetCurrentToken(); - if (!isImplicit) - { - while (current is DocumentEnd) - { - Skip(); - current = GetCurrentToken(); - } - } - - if (current == null) - { - throw new SyntaxErrorException("Reached the end of the stream while parsing a document start."); - } - - if (current is Scalar && (state == ParserState.ImplicitDocumentStart || state == ParserState.DocumentStart)) - { - isImplicit = true; - } - - // Parse an isImplicit document. - - if (isImplicit && !(current is VersionDirective || current is TagDirective || current is DocumentStart || current is StreamEnd || current is DocumentEnd) || current is BlockMappingStart) - { - var directives = new TagDirectiveCollection(); - ProcessDirectives(directives); - - states.Push(ParserState.DocumentEnd); - - state = ParserState.BlockNode; - - return new Events.DocumentStart(null, directives, true, current.Start, current.End); - } - - // Parse an explicit document. - - else if (!(current is StreamEnd || current is DocumentEnd)) - { - var start = current.Start; - var directives = new TagDirectiveCollection(); - var versionDirective = ProcessDirectives(directives); - - current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document start"); - - if (!(current is DocumentStart)) - { - throw new SemanticErrorException(current.Start, current.End, "Did not find expected ."); - } - - states.Push(ParserState.DocumentEnd); - - state = ParserState.DocumentContent; - - var end = current.End; - Skip(); - return new Events.DocumentStart(versionDirective, directives, false, start, end); - } - - // Parse the stream end. - - else - { - if (current is DocumentEnd) - { - Skip(); - } - state = ParserState.StreamEnd; - - current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document start"); - - var evt = new Events.StreamEnd(current.Start, current.End); - // Do not call skip here because that would throw an exception - if (scanner.MoveNextWithoutConsuming()) - { - throw new InvalidOperationException("The scanner should contain no more tokens."); - } - return evt; - } - } - - /// - /// Parse directives. - /// - private VersionDirective? ProcessDirectives(TagDirectiveCollection tags) - { - var hasOwnDirectives = false; - VersionDirective? localVersion = null; - - while (true) - { - if (GetCurrentToken() is VersionDirective currentVersion) - { - if (version != null) - { - throw new SemanticErrorException(currentVersion.Start, currentVersion.End, "Found duplicate %YAML directive."); - } - - if (currentVersion.Version.Major != Constants.MajorVersion || currentVersion.Version.Minor > Constants.MinorVersion) - { - throw new SemanticErrorException(currentVersion.Start, currentVersion.End, "Found incompatible YAML document."); - } - - localVersion = version = currentVersion; - hasOwnDirectives = true; - } - else if (GetCurrentToken() is TagDirective tag) - { - if (tags.Contains(tag.Handle)) - { - throw new SemanticErrorException(tag.Start, tag.End, "Found duplicate %TAG directive."); - } - tags.Add(tag); - hasOwnDirectives = true; - } - - // Starting from v1.2, it is not permitted to use tag shorthands for multiple documents in a stream. - else if (GetCurrentToken() is DocumentStart && (version == null || (version.Version.Major == 1 && version.Version.Minor > 1))) - { - if (GetCurrentToken() is DocumentStart && (version == null)) - { - version = new VersionDirective(new Version(1, 2)); - } - - hasOwnDirectives = true; - break; - } - else - { - break; - } - - Skip(); - } - - AddTagDirectives(tags, Constants.DefaultTagDirectives); - - if (hasOwnDirectives) - { - tagDirectives.Clear(); - } - - AddTagDirectives(tagDirectives, tags); - - return localVersion; - } - - private static void AddTagDirectives(TagDirectiveCollection directives, IEnumerable source) - { - foreach (var directive in source) - { - if (!directives.Contains(directive)) - { - directives.Add(directive); - } - } - } - - /// - /// Parse the productions: - /// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* - /// *********** - /// - private ParsingEvent ParseDocumentContent() - { - if ( - GetCurrentToken() is VersionDirective || - GetCurrentToken() is TagDirective || - GetCurrentToken() is DocumentStart || - GetCurrentToken() is DocumentEnd || - GetCurrentToken() is StreamEnd - ) - { - state = states.Pop(); - return ProcessEmptyScalar(scanner.CurrentPosition); - } - else - { - return ParseNode(true, false); - } - } - - /// - /// Generate an empty scalar event. - /// - private static ParsingEvent ProcessEmptyScalar(in Mark position) - { - return new Events.Scalar(AnchorName.Empty, TagName.Empty, string.Empty, ScalarStyle.Plain, true, false, position, position); - } - - /// - /// Parse the productions: - /// block_node_or_indentless_sequence ::= - /// ALIAS - /// ***** - /// | properties (block_content | indentless_block_sequence)? - /// ********** * - /// | block_content | indentless_block_sequence - /// * - /// block_node ::= ALIAS - /// ***** - /// | properties block_content? - /// ********** * - /// | block_content - /// * - /// flow_node ::= ALIAS - /// ***** - /// | properties flow_content? - /// ********** * - /// | flow_content - /// * - /// properties ::= TAG ANCHOR? | ANCHOR TAG? - /// ************************* - /// block_content ::= block_collection | flow_collection | SCALAR - /// ****** - /// flow_content ::= flow_collection | SCALAR - /// ****** - /// - private ParsingEvent ParseNode(bool isBlock, bool isIndentlessSequence) - { - if (GetCurrentToken() is Error errorToken) - { - throw new SemanticErrorException(errorToken.Start, errorToken.End, errorToken.Value); - } - - var current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a node"); - if (current is AnchorAlias alias) - { - state = states.Pop(); - ParsingEvent evt = new Events.AnchorAlias(alias.Value, alias.Start, alias.End); - Skip(); - return evt; - } - - var start = current.Start; - - var anchorName = AnchorName.Empty; - var tagName = TagName.Empty; - Anchor? lastAnchor = null; - Tag? lastTag = null; - - // The anchor and the tag can be in any order. This loop repeats at most twice. - while (true) - { - if (anchorName.IsEmpty && current is Anchor anchor) - { - lastAnchor = anchor; - anchorName = anchor.Value; - Skip(); - } - else if (tagName.IsEmpty && current is Tag tag) - { - lastTag = tag; - if (string.IsNullOrEmpty(tag.Handle)) - { - tagName = new TagName(tag.Suffix); - } - else if (tagDirectives.Contains(tag.Handle)) - { - tagName = new TagName(string.Concat(tagDirectives[tag.Handle].Prefix, tag.Suffix)); - } - else - { - throw new SemanticErrorException(tag.Start, tag.End, "While parsing a node, found undefined tag handle."); - } - - Skip(); - } - else if (current is Anchor secondAnchor) - { - throw new SemanticErrorException(secondAnchor.Start, secondAnchor.End, "While parsing a node, found more than one anchor."); - } - else if (current is AnchorAlias anchorAlias) - { - throw new SemanticErrorException(anchorAlias.Start, anchorAlias.End, "While parsing a node, did not find expected token."); - } - else if (current is Error error) - { - if (lastTag != null && lastAnchor != null && !anchorName.IsEmpty) - { - return new Events.Scalar(anchorName, default, string.Empty, default, false, false, lastAnchor.Start, lastAnchor.End); - } - throw new SemanticErrorException(error.Start, error.End, error.Value); - } - else - { - break; - } - - current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a node"); - } - - var isImplicit = tagName.IsEmpty; - - if (isIndentlessSequence && GetCurrentToken() is BlockEntry) - { - state = ParserState.IndentlessSequenceEntry; - - return new Events.SequenceStart( - anchorName, - tagName, - isImplicit, - SequenceStyle.Block, - start, - current.End - ); - } - else - { - if (current is Scalar scalar) - { - var isPlainImplicit = false; - var isQuotedImplicit = false; - if ((scalar.Style == ScalarStyle.Plain && tagName.IsEmpty) || tagName.IsNonSpecific) - { - isPlainImplicit = true; - } - else if (tagName.IsEmpty) - { - isQuotedImplicit = true; - } - - state = states.Pop(); - Skip(); - - ParsingEvent evt = new Events.Scalar(anchorName, tagName, scalar.Value, scalar.Style, isPlainImplicit, isQuotedImplicit, start, scalar.End, scalar.IsKey); - - // Read next token to ensure the error case spec test 'CXX2': - // "Mapping with anchor on document start line". - - if (!anchorName.IsEmpty && scanner.MoveNextWithoutConsuming()) - { - currentToken = scanner.Current; - if (currentToken is Error) - { - errorToken = (currentToken as Error)!; - throw new SemanticErrorException(errorToken.Start, errorToken.End, errorToken.Value); - } - } - - // Read next token to ensure the error case spec test 'T833': - // "Flow mapping missing a separating comma". - - if (state == ParserState.FlowMappingKey && scanner.MoveNextWithoutConsuming()) - { - currentToken = scanner.Current; - if (currentToken != null && !(currentToken is FlowEntry) && !(currentToken is FlowMappingEnd)) - { - throw new SemanticErrorException(currentToken.Start, currentToken.End, "While parsing a flow mapping, did not find expected ',' or '}'."); - } - } - - return evt; - } - - if (current is FlowSequenceStart flowSequenceStart) - { - state = ParserState.FlowSequenceFirstEntry; - return new Events.SequenceStart(anchorName, tagName, isImplicit, SequenceStyle.Flow, start, flowSequenceStart.End); - } - - if (current is FlowMappingStart flowMappingStart) - { - state = ParserState.FlowMappingFirstKey; - return new Events.MappingStart(anchorName, tagName, isImplicit, MappingStyle.Flow, start, flowMappingStart.End); - } - - if (isBlock) - { - if (current is BlockSequenceStart blockSequenceStart) - { - state = ParserState.BlockSequenceFirstEntry; - return new Events.SequenceStart(anchorName, tagName, isImplicit, SequenceStyle.Block, start, blockSequenceStart.End); - } - - if (current is BlockMappingStart blockMappingStart) - { - state = ParserState.BlockMappingFirstKey; - return new Events.MappingStart(anchorName, tagName, isImplicit, MappingStyle.Block, start, blockMappingStart.End); - } - } - - if (!anchorName.IsEmpty || !tagName.IsEmpty) - { - state = states.Pop(); - return new Events.Scalar(anchorName, tagName, string.Empty, ScalarStyle.Plain, isImplicit, false, start, current.End); - } - - throw new SemanticErrorException(current.Start, current.End, "While parsing a node, did not find expected node content."); - } - } - - /// - /// Parse the productions: - /// implicit_document ::= block_node DOCUMENT-END* - /// ************* - /// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* - /// ************* - /// - - private ParsingEvent ParseDocumentEnd() - { - var current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document end"); - - var isImplicit = true; - var start = current.Start; - var end = start; - - if (current is DocumentEnd) - { - end = current.End; - Skip(); - isImplicit = false; - } - else if (!(currentToken is StreamEnd || currentToken is DocumentStart || currentToken is FlowSequenceEnd || currentToken is VersionDirective || - (Current is Events.Scalar && currentToken is Error))) - { - throw new SemanticErrorException(start, end, "Did not find expected ."); - } - - if (version != null && version.Version.Major == 1 && version.Version.Minor > 1) - { - version = null; - } - state = ParserState.DocumentStart; - return new Events.DocumentEnd(isImplicit, start, end); - } - - /// - /// Parse the productions: - /// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END - /// ******************** *********** * ********* - /// - - private ParsingEvent ParseBlockSequenceEntry(bool isFirst) - { - if (isFirst) - { - GetCurrentToken(); - Skip(); - } - - var current = GetCurrentToken(); - if (current is BlockEntry blockEntry) - { - var mark = blockEntry.End; - - Skip(); - current = GetCurrentToken(); - if (!(current is BlockEntry || current is BlockEnd)) - { - states.Push(ParserState.BlockSequenceEntry); - return ParseNode(true, false); - } - else - { - state = ParserState.BlockSequenceEntry; - return ProcessEmptyScalar(mark); - } - } - else if (current is BlockEnd blockEnd) - { - state = states.Pop(); - ParsingEvent evt = new Events.SequenceEnd(blockEnd.Start, blockEnd.End); - Skip(); - return evt; - } - else - { - throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "While parsing a block collection, did not find expected '-' indicator."); - } - } - - /// - /// Parse the productions: - /// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ - /// *********** * - /// - private ParsingEvent ParseIndentlessSequenceEntry() - { - var current = GetCurrentToken(); - if (current is BlockEntry blockEntry) - { - var mark = blockEntry.End; - Skip(); - - current = GetCurrentToken(); - if (!(current is BlockEntry || current is Key || current is Value || current is BlockEnd)) - { - states.Push(ParserState.IndentlessSequenceEntry); - return ParseNode(true, false); - } - else - { - state = ParserState.IndentlessSequenceEntry; - return ProcessEmptyScalar(mark); - } - } - else - { - state = states.Pop(); - return new Events.SequenceEnd(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty); - } - } - - /// - /// Parse the productions: - /// block_mapping ::= BLOCK-MAPPING_START - /// ******************* - /// ((KEY block_node_or_indentless_sequence?)? - /// *** * - /// (VALUE block_node_or_indentless_sequence?)?)* - /// - /// BLOCK-END - /// ********* - /// - private ParsingEvent ParseBlockMappingKey(bool isFirst) - { - if (isFirst) - { - GetCurrentToken(); - Skip(); - } - - var current = GetCurrentToken(); - if (current is Key key) - { - var mark = key.End; - Skip(); - current = GetCurrentToken(); - if (!(current is Key || current is Value || current is BlockEnd)) - { - states.Push(ParserState.BlockMappingValue); - return ParseNode(true, true); - } - else - { - state = ParserState.BlockMappingValue; - return ProcessEmptyScalar(mark); - } - } - - else if (current is Value value) - { - Skip(); - return ProcessEmptyScalar(value.End); - } - - else if (current is AnchorAlias anchorAlias) - { - Skip(); - return new Events.AnchorAlias(anchorAlias.Value, anchorAlias.Start, anchorAlias.End); - } - - else if (current is BlockEnd blockEnd) - { - state = states.Pop(); - ParsingEvent evt = new Events.MappingEnd(blockEnd.Start, blockEnd.End); - Skip(); - return evt; - } - - else if (GetCurrentToken() is Error error) - { - throw new SyntaxErrorException(error.Start, error.End, error.Value); - } - - else - { - throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "While parsing a block mapping, did not find expected key."); - } - } - - /// - /// Parse the productions: - /// block_mapping ::= BLOCK-MAPPING_START - /// - /// ((KEY block_node_or_indentless_sequence?)? - /// - /// (VALUE block_node_or_indentless_sequence?)?)* - /// ***** * - /// BLOCK-END - /// - /// - private ParsingEvent ParseBlockMappingValue() - { - var current = GetCurrentToken(); - if (current is Value value) - { - var mark = value.End; - Skip(); - - current = GetCurrentToken(); - if (!(current is Key || current is Value || current is BlockEnd)) - { - states.Push(ParserState.BlockMappingKey); - return ParseNode(true, true); - } - else - { - state = ParserState.BlockMappingKey; - return ProcessEmptyScalar(mark); - } - } - else if (current is Error error) - { - throw new SemanticErrorException(error.Start, error.End, error.Value); - } - else - { - state = ParserState.BlockMappingKey; - return ProcessEmptyScalar(current?.Start ?? Mark.Empty); - } - } - - /// - /// Parse the productions: - /// flow_sequence ::= FLOW-SEQUENCE-START - /// ******************* - /// (flow_sequence_entry FLOW-ENTRY)* - /// * ********** - /// flow_sequence_entry? - /// * - /// FLOW-SEQUENCE-END - /// ***************** - /// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - /// * - /// - private ParsingEvent ParseFlowSequenceEntry(bool isFirst) - { - if (isFirst) - { - GetCurrentToken(); - Skip(); - } - - ParsingEvent evt; - var current = GetCurrentToken(); - if (!(current is FlowSequenceEnd)) - { - if (!isFirst) - { - if (current is FlowEntry) - { - Skip(); - current = GetCurrentToken(); - } - else - { - throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "While parsing a flow sequence, did not find expected ',' or ']'."); - } - } - - if (current is Key) - { - state = ParserState.FlowSequenceEntryMappingKey; - evt = new Events.MappingStart(AnchorName.Empty, TagName.Empty, true, MappingStyle.Flow); - Skip(); - return evt; - } - else if (!(current is FlowSequenceEnd)) - { - states.Push(ParserState.FlowSequenceEntry); - return ParseNode(false, false); - } - } - - state = states.Pop(); - evt = new Events.SequenceEnd(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty); - Skip(); - return evt; - } - - /// - /// Parse the productions: - /// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - /// *** * - /// - private ParsingEvent ParseFlowSequenceEntryMappingKey() - { - var current = GetCurrentToken(); - if (!(current is Value || current is FlowEntry || current is FlowSequenceEnd)) - { - states.Push(ParserState.FlowSequenceEntryMappingValue); - return ParseNode(false, false); - } - else - { - var mark = current?.End ?? Mark.Empty; - Skip(); - state = ParserState.FlowSequenceEntryMappingValue; - return ProcessEmptyScalar(mark); - } - } - - /// - /// Parse the productions: - /// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - /// ***** * - /// - private ParsingEvent ParseFlowSequenceEntryMappingValue() - { - var current = GetCurrentToken(); - if (current is Value) - { - Skip(); - current = GetCurrentToken(); - if (!(current is FlowEntry || current is FlowSequenceEnd)) - { - states.Push(ParserState.FlowSequenceEntryMappingEnd); - return ParseNode(false, false); - } - } - state = ParserState.FlowSequenceEntryMappingEnd; - return ProcessEmptyScalar(current?.Start ?? Mark.Empty); - } - - /// - /// Parse the productions: - /// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - /// * - /// - private ParsingEvent ParseFlowSequenceEntryMappingEnd() - { - state = ParserState.FlowSequenceEntry; - var current = GetCurrentToken(); - return new Events.MappingEnd(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty); - } - - /// - /// Parse the productions: - /// flow_mapping ::= FLOW-MAPPING-START - /// ****************** - /// (flow_mapping_entry FLOW-ENTRY)* - /// * ********** - /// flow_mapping_entry? - /// ****************** - /// FLOW-MAPPING-END - /// **************** - /// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - /// * *** * - /// - private ParsingEvent ParseFlowMappingKey(bool isFirst) - { - if (isFirst) - { - GetCurrentToken(); - Skip(); - } - - var current = GetCurrentToken(); - if (!(current is FlowMappingEnd)) - { - if (!isFirst) - { - if (current is FlowEntry) - { - Skip(); - current = GetCurrentToken(); - } - else if (!(current is Scalar)) - { - throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "While parsing a flow mapping, did not find expected ',' or '}'."); - } - } - - if (current is Key) - { - Skip(); - - current = GetCurrentToken(); - if (!(current is Value || current is FlowEntry || current is FlowMappingEnd)) - { - states.Push(ParserState.FlowMappingValue); - return ParseNode(false, false); - } - else - { - state = ParserState.FlowMappingValue; - return ProcessEmptyScalar(current?.Start ?? Mark.Empty); - } - } - else if (current is Scalar) - { - states.Push(ParserState.FlowMappingValue); - return ParseNode(false, false); - } - else if (!(current is FlowMappingEnd)) - { - states.Push(ParserState.FlowMappingEmptyValue); - return ParseNode(false, false); - } - } - - state = states.Pop(); - Skip(); - return new Events.MappingEnd(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty); - } - - /// - /// Parse the productions: - /// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - /// * ***** * - /// - private ParsingEvent ParseFlowMappingValue(bool isEmpty) - { - var current = GetCurrentToken(); - if (!isEmpty && current is Value) - { - Skip(); - current = GetCurrentToken(); - if (!(current is FlowEntry || current is FlowMappingEnd)) - { - states.Push(ParserState.FlowMappingKey); - return ParseNode(false, false); - } - } - - state = ParserState.FlowMappingKey; - - if (!isEmpty && current is Scalar scalar) - { - Skip(); - return new Events.Scalar(AnchorName.Empty, TagName.Empty, scalar.Value, scalar.Style, false, false, current.Start, scalar.End); - } - - return ProcessEmptyScalar(current?.Start ?? Mark.Empty); - } - - private class EventQueue - { - // This class is specialized for our specific use case where there are exactly two priority levels. - // If more levels are required, a more generic implementation should be used instead. - private readonly Queue highPriorityEvents = new Queue(); - private readonly Queue normalPriorityEvents = new Queue(); - - public void Enqueue(ParsingEvent @event) - { - switch (@event.Type) - { - case Events.EventType.StreamStart: - case Events.EventType.DocumentStart: - highPriorityEvents.Enqueue(@event); - break; - - default: - normalPriorityEvents.Enqueue(@event); - break; - } - } - - public ParsingEvent Dequeue() - { - return highPriorityEvents.Count > 0 - ? highPriorityEvents.Dequeue() - : normalPriorityEvents.Dequeue(); - } - - public int Count - { - get - { - return highPriorityEvents.Count + normalPriorityEvents.Count; - } - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using YamlDotNet.Core.Tokens; +using MappingStyle = YamlDotNet.Core.Events.MappingStyle; +using ParsingEvent = YamlDotNet.Core.Events.ParsingEvent; +using SequenceStyle = YamlDotNet.Core.Events.SequenceStyle; + +namespace YamlDotNet.Core +{ + /// + /// Parses YAML streams. + /// + public class Parser : IParser + { + private readonly Stack states = new Stack(); + private readonly TagDirectiveCollection tagDirectives = new TagDirectiveCollection(); + private ParserState state; + + private readonly IScanner scanner; + private Token? currentToken; + private VersionDirective? version; + + private Token? GetCurrentToken() + { + if (currentToken == null) + { + while (scanner.MoveNextWithoutConsuming()) + { + currentToken = scanner.Current; + + if (currentToken is Comment commentToken) + { + pendingEvents.Enqueue(new Events.Comment(commentToken.Value, commentToken.IsInline, commentToken.Start, commentToken.End)); + scanner.ConsumeCurrent(); + } + else + { + break; + } + } + } + return currentToken; + } + + /// + /// Initializes a new instance of the class. + /// + /// The input where the YAML stream is to be read. + public Parser(TextReader input) + : this(new Scanner(input)) + { + } + + /// + /// Initializes a new instance of the class. + /// + public Parser(IScanner scanner) + { + this.scanner = scanner; + } + + /// + /// Gets the current event. + /// + public ParsingEvent? Current { get; private set; } + + private readonly EventQueue pendingEvents = new EventQueue(); + + /// + /// Moves to the next event. + /// + /// Returns true if there are more events available, otherwise returns false. + public bool MoveNext() + { + // No events after the end of the stream or error. + if (state == ParserState.StreamEnd) + { + Current = null; + return false; + } + else if (pendingEvents.Count == 0) + { + // Generate the next event. + pendingEvents.Enqueue(StateMachine()); + } + + Current = pendingEvents.Dequeue(); + return true; + } + + private ParsingEvent StateMachine() + { + switch (state) + { + case ParserState.StreamStart: + return ParseStreamStart(); + + case ParserState.ImplicitDocumentStart: + return ParseDocumentStart(true); + + case ParserState.DocumentStart: + return ParseDocumentStart(false); + + case ParserState.DocumentContent: + return ParseDocumentContent(); + + case ParserState.DocumentEnd: + return ParseDocumentEnd(); + + case ParserState.BlockNode: + return ParseNode(true, false); + + case ParserState.BlockNodeOrIndentlessSequence: + return ParseNode(true, true); + + case ParserState.FlowNode: + return ParseNode(false, false); + + case ParserState.BlockSequenceFirstEntry: + return ParseBlockSequenceEntry(true); + + case ParserState.BlockSequenceEntry: + return ParseBlockSequenceEntry(false); + + case ParserState.IndentlessSequenceEntry: + return ParseIndentlessSequenceEntry(); + + case ParserState.BlockMappingFirstKey: + return ParseBlockMappingKey(true); + + case ParserState.BlockMappingKey: + return ParseBlockMappingKey(false); + + case ParserState.BlockMappingValue: + return ParseBlockMappingValue(); + + case ParserState.FlowSequenceFirstEntry: + return ParseFlowSequenceEntry(true); + + case ParserState.FlowSequenceEntry: + return ParseFlowSequenceEntry(false); + + case ParserState.FlowSequenceEntryMappingKey: + return ParseFlowSequenceEntryMappingKey(); + + case ParserState.FlowSequenceEntryMappingValue: + return ParseFlowSequenceEntryMappingValue(); + + case ParserState.FlowSequenceEntryMappingEnd: + return ParseFlowSequenceEntryMappingEnd(); + + case ParserState.FlowMappingFirstKey: + return ParseFlowMappingKey(true); + + case ParserState.FlowMappingKey: + return ParseFlowMappingKey(false); + + case ParserState.FlowMappingValue: + return ParseFlowMappingValue(false); + + case ParserState.FlowMappingEmptyValue: + return ParseFlowMappingValue(true); + + default: + Debug.Assert(false, "Invalid state"); // Invalid state. + throw new InvalidOperationException(); + } + } + + private void Skip() + { + if (currentToken != null) + { + currentToken = null; + scanner.ConsumeCurrent(); + } + } + + /// + /// Parse the production: + /// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END + /// ************ + /// + private ParsingEvent ParseStreamStart() + { + var current = GetCurrentToken(); + + if (!(current is StreamStart streamStart)) + { + throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "Did not find expected ."); + } + Skip(); + + state = ParserState.ImplicitDocumentStart; + return new Events.StreamStart(streamStart.Start, streamStart.End); + } + + /// + /// Parse the productions: + /// implicit_document ::= block_node DOCUMENT-END* + /// * + /// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* + /// ************************* + /// + private ParsingEvent ParseDocumentStart(bool isImplicit) + { + if (currentToken is VersionDirective) + { + // EB22 + throw new SyntaxErrorException("While parsing a document start node, could not find document end marker before version directive."); + } + + // Parse extra document end indicators. + + var current = GetCurrentToken(); + if (!isImplicit) + { + while (current is DocumentEnd) + { + Skip(); + current = GetCurrentToken(); + } + } + + if (current == null) + { + throw new SyntaxErrorException("Reached the end of the stream while parsing a document start."); + } + + if (current is Scalar && (state == ParserState.ImplicitDocumentStart || state == ParserState.DocumentStart)) + { + isImplicit = true; + } + + // Parse an isImplicit document. + + if (isImplicit && !(current is VersionDirective || current is TagDirective || current is DocumentStart || current is StreamEnd || current is DocumentEnd) || current is BlockMappingStart) + { + var directives = new TagDirectiveCollection(); + ProcessDirectives(directives); + + states.Push(ParserState.DocumentEnd); + + state = ParserState.BlockNode; + + return new Events.DocumentStart(null, directives, true, current.Start, current.End); + } + + // Parse an explicit document. + + else if (!(current is StreamEnd || current is DocumentEnd)) + { + var start = current.Start; + var directives = new TagDirectiveCollection(); + var versionDirective = ProcessDirectives(directives); + + current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document start"); + + if (!(current is DocumentStart)) + { + throw new SemanticErrorException(current.Start, current.End, "Did not find expected ."); + } + + states.Push(ParserState.DocumentEnd); + + state = ParserState.DocumentContent; + + var end = current.End; + Skip(); + return new Events.DocumentStart(versionDirective, directives, false, start, end); + } + + // Parse the stream end. + + else + { + if (current is DocumentEnd) + { + Skip(); + } + state = ParserState.StreamEnd; + + current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document start"); + + var evt = new Events.StreamEnd(current.Start, current.End); + // Do not call skip here because that would throw an exception + if (scanner.MoveNextWithoutConsuming()) + { + throw new InvalidOperationException("The scanner should contain no more tokens."); + } + return evt; + } + } + + /// + /// Parse directives. + /// + private VersionDirective? ProcessDirectives(TagDirectiveCollection tags) + { + var hasOwnDirectives = false; + VersionDirective? localVersion = null; + + while (true) + { + if (GetCurrentToken() is VersionDirective currentVersion) + { + if (version != null) + { + throw new SemanticErrorException(currentVersion.Start, currentVersion.End, "Found duplicate %YAML directive."); + } + + if (currentVersion.Version.Major != Constants.MajorVersion || currentVersion.Version.Minor > Constants.MinorVersion) + { + throw new SemanticErrorException(currentVersion.Start, currentVersion.End, "Found incompatible YAML document."); + } + + localVersion = version = currentVersion; + hasOwnDirectives = true; + } + else if (GetCurrentToken() is TagDirective tag) + { + if (tags.Contains(tag.Handle)) + { + throw new SemanticErrorException(tag.Start, tag.End, "Found duplicate %TAG directive."); + } + tags.Add(tag); + hasOwnDirectives = true; + } + + // Starting from v1.2, it is not permitted to use tag shorthands for multiple documents in a stream. + else if (GetCurrentToken() is DocumentStart && (version == null || (version.Version.Major == 1 && version.Version.Minor > 1))) + { + if (GetCurrentToken() is DocumentStart && (version == null)) + { + version = new VersionDirective(new Version(1, 2)); + } + + hasOwnDirectives = true; + break; + } + else + { + break; + } + + Skip(); + } + + AddTagDirectives(tags, Constants.DefaultTagDirectives); + + if (hasOwnDirectives) + { + tagDirectives.Clear(); + } + + AddTagDirectives(tagDirectives, tags); + + return localVersion; + } + + private static void AddTagDirectives(TagDirectiveCollection directives, IEnumerable source) + { + foreach (var directive in source) + { + if (!directives.Contains(directive)) + { + directives.Add(directive); + } + } + } + + /// + /// Parse the productions: + /// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* + /// *********** + /// + private ParsingEvent ParseDocumentContent() + { + if ( + GetCurrentToken() is VersionDirective || + GetCurrentToken() is TagDirective || + GetCurrentToken() is DocumentStart || + GetCurrentToken() is DocumentEnd || + GetCurrentToken() is StreamEnd + ) + { + state = states.Pop(); + return ProcessEmptyScalar(scanner.CurrentPosition); + } + else + { + return ParseNode(true, false); + } + } + + /// + /// Generate an empty scalar event. + /// + private static ParsingEvent ProcessEmptyScalar(in Mark position) + { + return new Events.Scalar(AnchorName.Empty, TagName.Empty, string.Empty, ScalarStyle.Plain, true, false, position, position); + } + + /// + /// Parse the productions: + /// block_node_or_indentless_sequence ::= + /// ALIAS + /// ***** + /// | properties (block_content | indentless_block_sequence)? + /// ********** * + /// | block_content | indentless_block_sequence + /// * + /// block_node ::= ALIAS + /// ***** + /// | properties block_content? + /// ********** * + /// | block_content + /// * + /// flow_node ::= ALIAS + /// ***** + /// | properties flow_content? + /// ********** * + /// | flow_content + /// * + /// properties ::= TAG ANCHOR? | ANCHOR TAG? + /// ************************* + /// block_content ::= block_collection | flow_collection | SCALAR + /// ****** + /// flow_content ::= flow_collection | SCALAR + /// ****** + /// + private ParsingEvent ParseNode(bool isBlock, bool isIndentlessSequence) + { + if (GetCurrentToken() is Error errorToken) + { + throw new SemanticErrorException(errorToken.Start, errorToken.End, errorToken.Value); + } + + var current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a node"); + if (current is AnchorAlias alias) + { + state = states.Pop(); + ParsingEvent evt = new Events.AnchorAlias(alias.Value, alias.Start, alias.End); + Skip(); + return evt; + } + + var start = current.Start; + + var anchorName = AnchorName.Empty; + var tagName = TagName.Empty; + Anchor? lastAnchor = null; + Tag? lastTag = null; + + // The anchor and the tag can be in any order. This loop repeats at most twice. + while (true) + { + if (anchorName.IsEmpty && current is Anchor anchor) + { + lastAnchor = anchor; + anchorName = anchor.Value; + Skip(); + } + else if (tagName.IsEmpty && current is Tag tag) + { + lastTag = tag; + if (string.IsNullOrEmpty(tag.Handle)) + { + tagName = new TagName(tag.Suffix); + } + else if (tagDirectives.Contains(tag.Handle)) + { + tagName = new TagName(string.Concat(tagDirectives[tag.Handle].Prefix, tag.Suffix)); + } + else + { + throw new SemanticErrorException(tag.Start, tag.End, "While parsing a node, found undefined tag handle."); + } + + Skip(); + } + else if (current is Anchor secondAnchor) + { + throw new SemanticErrorException(secondAnchor.Start, secondAnchor.End, "While parsing a node, found more than one anchor."); + } + else if (current is AnchorAlias anchorAlias) + { + throw new SemanticErrorException(anchorAlias.Start, anchorAlias.End, "While parsing a node, did not find expected token."); + } + else if (current is Error error) + { + if (lastTag != null && lastAnchor != null && !anchorName.IsEmpty) + { + return new Events.Scalar(anchorName, default, string.Empty, default, false, false, lastAnchor.Start, lastAnchor.End); + } + throw new SemanticErrorException(error.Start, error.End, error.Value); + } + else + { + break; + } + + current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a node"); + } + + var isImplicit = tagName.IsEmpty; + + if (isIndentlessSequence && GetCurrentToken() is BlockEntry) + { + state = ParserState.IndentlessSequenceEntry; + + return new Events.SequenceStart( + anchorName, + tagName, + isImplicit, + SequenceStyle.Block, + start, + current.End + ); + } + else + { + if (current is Scalar scalar) + { + var isPlainImplicit = false; + var isQuotedImplicit = false; + if ((scalar.Style == ScalarStyle.Plain && tagName.IsEmpty) || tagName.IsNonSpecific) + { + isPlainImplicit = true; + } + else if (tagName.IsEmpty) + { + isQuotedImplicit = true; + } + + state = states.Pop(); + Skip(); + + ParsingEvent evt = new Events.Scalar(anchorName, tagName, scalar.Value, scalar.Style, isPlainImplicit, isQuotedImplicit, start, scalar.End, scalar.IsKey); + + // Read next token to ensure the error case spec test 'CXX2': + // "Mapping with anchor on document start line". + + if (!anchorName.IsEmpty && scanner.MoveNextWithoutConsuming()) + { + currentToken = scanner.Current; + if (currentToken is Error) + { + errorToken = (currentToken as Error)!; + throw new SemanticErrorException(errorToken.Start, errorToken.End, errorToken.Value); + } + } + + // Read next token to ensure the error case spec test 'T833': + // "Flow mapping missing a separating comma". + + if (state == ParserState.FlowMappingKey && scanner.MoveNextWithoutConsuming()) + { + currentToken = scanner.Current; + if (currentToken != null && !(currentToken is FlowEntry) && !(currentToken is FlowMappingEnd)) + { + throw new SemanticErrorException(currentToken.Start, currentToken.End, "While parsing a flow mapping, did not find expected ',' or '}'."); + } + } + + return evt; + } + + if (current is FlowSequenceStart flowSequenceStart) + { + state = ParserState.FlowSequenceFirstEntry; + return new Events.SequenceStart(anchorName, tagName, isImplicit, SequenceStyle.Flow, start, flowSequenceStart.End); + } + + if (current is FlowMappingStart flowMappingStart) + { + state = ParserState.FlowMappingFirstKey; + return new Events.MappingStart(anchorName, tagName, isImplicit, MappingStyle.Flow, start, flowMappingStart.End); + } + + if (isBlock) + { + if (current is BlockSequenceStart blockSequenceStart) + { + state = ParserState.BlockSequenceFirstEntry; + return new Events.SequenceStart(anchorName, tagName, isImplicit, SequenceStyle.Block, start, blockSequenceStart.End); + } + + if (current is BlockMappingStart blockMappingStart) + { + state = ParserState.BlockMappingFirstKey; + return new Events.MappingStart(anchorName, tagName, isImplicit, MappingStyle.Block, start, blockMappingStart.End); + } + } + + if (!anchorName.IsEmpty || !tagName.IsEmpty) + { + state = states.Pop(); + return new Events.Scalar(anchorName, tagName, string.Empty, ScalarStyle.Plain, isImplicit, false, start, current.End); + } + + throw new SemanticErrorException(current.Start, current.End, "While parsing a node, did not find expected node content."); + } + } + + /// + /// Parse the productions: + /// implicit_document ::= block_node DOCUMENT-END* + /// ************* + /// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* + /// ************* + /// + + private ParsingEvent ParseDocumentEnd() + { + var current = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document end"); + + var isImplicit = true; + var start = current.Start; + var end = start; + + if (current is DocumentEnd) + { + end = current.End; + Skip(); + isImplicit = false; + } + else if (!(currentToken is StreamEnd || currentToken is DocumentStart || currentToken is FlowSequenceEnd || currentToken is VersionDirective || + (Current is Events.Scalar && currentToken is Error))) + { + throw new SemanticErrorException(start, end, "Did not find expected ."); + } + + if (version != null && version.Version.Major == 1 && version.Version.Minor > 1) + { + version = null; + } + state = ParserState.DocumentStart; + return new Events.DocumentEnd(isImplicit, start, end); + } + + /// + /// Parse the productions: + /// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END + /// ******************** *********** * ********* + /// + + private ParsingEvent ParseBlockSequenceEntry(bool isFirst) + { + if (isFirst) + { + GetCurrentToken(); + Skip(); + } + + var current = GetCurrentToken(); + if (current is BlockEntry blockEntry) + { + var mark = blockEntry.End; + + Skip(); + current = GetCurrentToken(); + if (!(current is BlockEntry || current is BlockEnd)) + { + states.Push(ParserState.BlockSequenceEntry); + return ParseNode(true, false); + } + else + { + state = ParserState.BlockSequenceEntry; + return ProcessEmptyScalar(mark); + } + } + else if (current is BlockEnd blockEnd) + { + state = states.Pop(); + ParsingEvent evt = new Events.SequenceEnd(blockEnd.Start, blockEnd.End); + Skip(); + return evt; + } + else + { + throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "While parsing a block collection, did not find expected '-' indicator."); + } + } + + /// + /// Parse the productions: + /// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ + /// *********** * + /// + private ParsingEvent ParseIndentlessSequenceEntry() + { + var current = GetCurrentToken(); + if (current is BlockEntry blockEntry) + { + var mark = blockEntry.End; + Skip(); + + current = GetCurrentToken(); + if (!(current is BlockEntry || current is Key || current is Value || current is BlockEnd)) + { + states.Push(ParserState.IndentlessSequenceEntry); + return ParseNode(true, false); + } + else + { + state = ParserState.IndentlessSequenceEntry; + return ProcessEmptyScalar(mark); + } + } + else + { + state = states.Pop(); + return new Events.SequenceEnd(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty); + } + } + + /// + /// Parse the productions: + /// block_mapping ::= BLOCK-MAPPING_START + /// ******************* + /// ((KEY block_node_or_indentless_sequence?)? + /// *** * + /// (VALUE block_node_or_indentless_sequence?)?)* + /// + /// BLOCK-END + /// ********* + /// + private ParsingEvent ParseBlockMappingKey(bool isFirst) + { + if (isFirst) + { + GetCurrentToken(); + Skip(); + } + + var current = GetCurrentToken(); + if (current is Key key) + { + var mark = key.End; + Skip(); + current = GetCurrentToken(); + if (!(current is Key || current is Value || current is BlockEnd)) + { + states.Push(ParserState.BlockMappingValue); + return ParseNode(true, true); + } + else + { + state = ParserState.BlockMappingValue; + return ProcessEmptyScalar(mark); + } + } + + else if (current is Value value) + { + Skip(); + return ProcessEmptyScalar(value.End); + } + + else if (current is AnchorAlias anchorAlias) + { + Skip(); + return new Events.AnchorAlias(anchorAlias.Value, anchorAlias.Start, anchorAlias.End); + } + + else if (current is BlockEnd blockEnd) + { + state = states.Pop(); + ParsingEvent evt = new Events.MappingEnd(blockEnd.Start, blockEnd.End); + Skip(); + return evt; + } + + else if (GetCurrentToken() is Error error) + { + throw new SyntaxErrorException(error.Start, error.End, error.Value); + } + + else + { + throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "While parsing a block mapping, did not find expected key."); + } + } + + /// + /// Parse the productions: + /// block_mapping ::= BLOCK-MAPPING_START + /// + /// ((KEY block_node_or_indentless_sequence?)? + /// + /// (VALUE block_node_or_indentless_sequence?)?)* + /// ***** * + /// BLOCK-END + /// + /// + private ParsingEvent ParseBlockMappingValue() + { + var current = GetCurrentToken(); + if (current is Value value) + { + var mark = value.End; + Skip(); + + current = GetCurrentToken(); + if (!(current is Key || current is Value || current is BlockEnd)) + { + states.Push(ParserState.BlockMappingKey); + return ParseNode(true, true); + } + else + { + state = ParserState.BlockMappingKey; + return ProcessEmptyScalar(mark); + } + } + else if (current is Error error) + { + throw new SemanticErrorException(error.Start, error.End, error.Value); + } + else + { + state = ParserState.BlockMappingKey; + return ProcessEmptyScalar(current?.Start ?? Mark.Empty); + } + } + + /// + /// Parse the productions: + /// flow_sequence ::= FLOW-SEQUENCE-START + /// ******************* + /// (flow_sequence_entry FLOW-ENTRY)* + /// * ********** + /// flow_sequence_entry? + /// * + /// FLOW-SEQUENCE-END + /// ***************** + /// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + /// * + /// + private ParsingEvent ParseFlowSequenceEntry(bool isFirst) + { + if (isFirst) + { + GetCurrentToken(); + Skip(); + } + + ParsingEvent evt; + var current = GetCurrentToken(); + if (!(current is FlowSequenceEnd)) + { + if (!isFirst) + { + if (current is FlowEntry) + { + Skip(); + current = GetCurrentToken(); + } + else + { + throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "While parsing a flow sequence, did not find expected ',' or ']'."); + } + } + + if (current is Key) + { + state = ParserState.FlowSequenceEntryMappingKey; + evt = new Events.MappingStart(AnchorName.Empty, TagName.Empty, true, MappingStyle.Flow); + Skip(); + return evt; + } + else if (!(current is FlowSequenceEnd)) + { + states.Push(ParserState.FlowSequenceEntry); + return ParseNode(false, false); + } + } + + state = states.Pop(); + evt = new Events.SequenceEnd(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty); + Skip(); + return evt; + } + + /// + /// Parse the productions: + /// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + /// *** * + /// + private ParsingEvent ParseFlowSequenceEntryMappingKey() + { + var current = GetCurrentToken(); + if (!(current is Value || current is FlowEntry || current is FlowSequenceEnd)) + { + states.Push(ParserState.FlowSequenceEntryMappingValue); + return ParseNode(false, false); + } + else + { + var mark = current?.End ?? Mark.Empty; + Skip(); + state = ParserState.FlowSequenceEntryMappingValue; + return ProcessEmptyScalar(mark); + } + } + + /// + /// Parse the productions: + /// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + /// ***** * + /// + private ParsingEvent ParseFlowSequenceEntryMappingValue() + { + var current = GetCurrentToken(); + if (current is Value) + { + Skip(); + current = GetCurrentToken(); + if (!(current is FlowEntry || current is FlowSequenceEnd)) + { + states.Push(ParserState.FlowSequenceEntryMappingEnd); + return ParseNode(false, false); + } + } + state = ParserState.FlowSequenceEntryMappingEnd; + return ProcessEmptyScalar(current?.Start ?? Mark.Empty); + } + + /// + /// Parse the productions: + /// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + /// * + /// + private ParsingEvent ParseFlowSequenceEntryMappingEnd() + { + state = ParserState.FlowSequenceEntry; + var current = GetCurrentToken(); + return new Events.MappingEnd(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty); + } + + /// + /// Parse the productions: + /// flow_mapping ::= FLOW-MAPPING-START + /// ****************** + /// (flow_mapping_entry FLOW-ENTRY)* + /// * ********** + /// flow_mapping_entry? + /// ****************** + /// FLOW-MAPPING-END + /// **************** + /// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + /// * *** * + /// + private ParsingEvent ParseFlowMappingKey(bool isFirst) + { + if (isFirst) + { + GetCurrentToken(); + Skip(); + } + + var current = GetCurrentToken(); + if (!(current is FlowMappingEnd)) + { + if (!isFirst) + { + if (current is FlowEntry) + { + Skip(); + current = GetCurrentToken(); + } + else if (!(current is Scalar)) + { + throw new SemanticErrorException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "While parsing a flow mapping, did not find expected ',' or '}'."); + } + } + + if (current is Key) + { + Skip(); + + current = GetCurrentToken(); + if (!(current is Value || current is FlowEntry || current is FlowMappingEnd)) + { + states.Push(ParserState.FlowMappingValue); + return ParseNode(false, false); + } + else + { + state = ParserState.FlowMappingValue; + return ProcessEmptyScalar(current?.Start ?? Mark.Empty); + } + } + else if (current is Scalar) + { + states.Push(ParserState.FlowMappingValue); + return ParseNode(false, false); + } + else if (!(current is FlowMappingEnd)) + { + states.Push(ParserState.FlowMappingEmptyValue); + return ParseNode(false, false); + } + } + + state = states.Pop(); + Skip(); + return new Events.MappingEnd(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty); + } + + /// + /// Parse the productions: + /// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + /// * ***** * + /// + private ParsingEvent ParseFlowMappingValue(bool isEmpty) + { + var current = GetCurrentToken(); + if (!isEmpty && current is Value) + { + Skip(); + current = GetCurrentToken(); + if (!(current is FlowEntry || current is FlowMappingEnd)) + { + states.Push(ParserState.FlowMappingKey); + return ParseNode(false, false); + } + } + + state = ParserState.FlowMappingKey; + + if (!isEmpty && current is Scalar scalar) + { + Skip(); + return new Events.Scalar(AnchorName.Empty, TagName.Empty, scalar.Value, scalar.Style, false, false, current.Start, scalar.End); + } + + return ProcessEmptyScalar(current?.Start ?? Mark.Empty); + } + + private class EventQueue + { + // This class is specialized for our specific use case where there are exactly two priority levels. + // If more levels are required, a more generic implementation should be used instead. + private readonly Queue highPriorityEvents = new Queue(); + private readonly Queue normalPriorityEvents = new Queue(); + + public void Enqueue(ParsingEvent @event) + { + switch (@event.Type) + { + case Events.EventType.StreamStart: + case Events.EventType.DocumentStart: + highPriorityEvents.Enqueue(@event); + break; + + default: + normalPriorityEvents.Enqueue(@event); + break; + } + } + + public ParsingEvent Dequeue() + { + return highPriorityEvents.Count > 0 + ? highPriorityEvents.Dequeue() + : normalPriorityEvents.Dequeue(); + } + + public int Count + { + get + { + return highPriorityEvents.Count + normalPriorityEvents.Count; + } + } + } + } +} diff --git a/YamlDotNet/Core/ParserExtensions.cs b/YamlDotNet/Core/ParserExtensions.cs index 3b0a98f69..674de2bd4 100644 --- a/YamlDotNet/Core/ParserExtensions.cs +++ b/YamlDotNet/Core/ParserExtensions.cs @@ -1,151 +1,151 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Core -{ - /// - /// Extension methods that provide useful abstractions over . - /// - public static class ParserExtensions - { - /// - /// Ensures that the current event is of the specified type, returns it and moves to the next event. - /// - /// Type of the . - /// Returns the current event. - /// If the current event is not of the specified type. - public static T Consume(this IParser parser) where T : ParsingEvent - { - var required = parser.Require(); - parser.MoveNext(); - return required; - } - - /// - /// Checks whether the current event is of the specified type. - /// If the event is of the specified type, returns it and moves to the next event. - /// Otherwise returns null. - /// - /// Type of the . - /// Returns true if the current event is of type T; otherwise returns null. - public static bool TryConsume(this IParser parser, [MaybeNullWhen(false)] out T @event) where T : ParsingEvent - { - if (parser.Accept(out @event!)) - { - parser.MoveNext(); - return true; - } - return false; - } - - /// - /// Enforces that the current event is of the specified type. - /// - /// Type of the . - /// Returns the current event. - /// If the current event is not of the specified type. - public static T Require(this IParser parser) where T : ParsingEvent - { - if (!parser.Accept(out var required)) - { - var @event = parser.Current; - if (@event == null) - { - throw new YamlException($"Expected '{typeof(T).Name}', got nothing."); - } - throw new YamlException(@event.Start, @event.End, $"Expected '{typeof(T).Name}', got '{@event.GetType().Name}' (at {@event.Start})."); - } - return required; - } - - /// - /// Checks whether the current event is of the specified type. - /// - /// Type of the event. - /// Returns true if the current event is of type . Otherwise returns false. - public static bool Accept(this IParser parser, [MaybeNullWhen(false)] out T @event) where T : ParsingEvent - { - if (parser.Current == null) - { - if (!parser.MoveNext()) - { - throw new EndOfStreamException(); - } - } - - if (parser.Current is T evt) - { - @event = evt; - return true; - } - else - { - @event = default!; - return false; - } - } - - /// - /// Skips the current event and any nested event. - /// - public static void SkipThisAndNestedEvents(this IParser parser) - { - var depth = 0; - do - { - var next = parser.Consume(); - depth += next.NestingIncrease; - } - while (depth > 0); - } - - [Obsolete("Please use Consume() instead")] - public static T Expect(this IParser parser) where T : ParsingEvent - { - return parser.Consume(); - } - - [Obsolete("Please use TryConsume(out var evt) instead")] - [return: MaybeNull] - public static T? Allow(this IParser parser) where T : ParsingEvent - { - return parser.TryConsume(out var @event) ? @event : default; - } - - [Obsolete("Please use Accept(out var evt) instead")] - [return: MaybeNull] - public static T? Peek(this IParser parser) where T : ParsingEvent - { - return parser.Accept(out var @event) ? @event : default; - } - - [Obsolete("Please use TryConsume(out var evt) or Accept(out var evt) instead")] - public static bool Accept(this IParser parser) where T : ParsingEvent - { - return Accept(parser, out var _); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Core +{ + /// + /// Extension methods that provide useful abstractions over . + /// + public static class ParserExtensions + { + /// + /// Ensures that the current event is of the specified type, returns it and moves to the next event. + /// + /// Type of the . + /// Returns the current event. + /// If the current event is not of the specified type. + public static T Consume(this IParser parser) where T : ParsingEvent + { + var required = parser.Require(); + parser.MoveNext(); + return required; + } + + /// + /// Checks whether the current event is of the specified type. + /// If the event is of the specified type, returns it and moves to the next event. + /// Otherwise returns null. + /// + /// Type of the . + /// Returns true if the current event is of type T; otherwise returns null. + public static bool TryConsume(this IParser parser, [MaybeNullWhen(false)] out T @event) where T : ParsingEvent + { + if (parser.Accept(out @event!)) + { + parser.MoveNext(); + return true; + } + return false; + } + + /// + /// Enforces that the current event is of the specified type. + /// + /// Type of the . + /// Returns the current event. + /// If the current event is not of the specified type. + public static T Require(this IParser parser) where T : ParsingEvent + { + if (!parser.Accept(out var required)) + { + var @event = parser.Current; + if (@event == null) + { + throw new YamlException($"Expected '{typeof(T).Name}', got nothing."); + } + throw new YamlException(@event.Start, @event.End, $"Expected '{typeof(T).Name}', got '{@event.GetType().Name}' (at {@event.Start})."); + } + return required; + } + + /// + /// Checks whether the current event is of the specified type. + /// + /// Type of the event. + /// Returns true if the current event is of type . Otherwise returns false. + public static bool Accept(this IParser parser, [MaybeNullWhen(false)] out T @event) where T : ParsingEvent + { + if (parser.Current == null) + { + if (!parser.MoveNext()) + { + throw new EndOfStreamException(); + } + } + + if (parser.Current is T evt) + { + @event = evt; + return true; + } + else + { + @event = default!; + return false; + } + } + + /// + /// Skips the current event and any nested event. + /// + public static void SkipThisAndNestedEvents(this IParser parser) + { + var depth = 0; + do + { + var next = parser.Consume(); + depth += next.NestingIncrease; + } + while (depth > 0); + } + + [Obsolete("Please use Consume() instead")] + public static T Expect(this IParser parser) where T : ParsingEvent + { + return parser.Consume(); + } + + [Obsolete("Please use TryConsume(out var evt) instead")] + [return: MaybeNull] + public static T? Allow(this IParser parser) where T : ParsingEvent + { + return parser.TryConsume(out var @event) ? @event : default; + } + + [Obsolete("Please use Accept(out var evt) instead")] + [return: MaybeNull] + public static T? Peek(this IParser parser) where T : ParsingEvent + { + return parser.Accept(out var @event) ? @event : default; + } + + [Obsolete("Please use TryConsume(out var evt) or Accept(out var evt) instead")] + public static bool Accept(this IParser parser) where T : ParsingEvent + { + return Accept(parser, out var _); + } + } +} diff --git a/YamlDotNet/Core/ParserState.cs b/YamlDotNet/Core/ParserState.cs index 9504b9ac1..45080821d 100644 --- a/YamlDotNet/Core/ParserState.cs +++ b/YamlDotNet/Core/ParserState.cs @@ -1,51 +1,51 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core -{ - internal enum ParserState - { - StreamStart, - StreamEnd, - ImplicitDocumentStart, - DocumentStart, - DocumentContent, - DocumentEnd, - BlockNode, - BlockNodeOrIndentlessSequence, - FlowNode, - BlockSequenceFirstEntry, - BlockSequenceEntry, - IndentlessSequenceEntry, - BlockMappingFirstKey, - BlockMappingKey, - BlockMappingValue, - FlowSequenceFirstEntry, - FlowSequenceEntry, - FlowSequenceEntryMappingKey, - FlowSequenceEntryMappingValue, - FlowSequenceEntryMappingEnd, - FlowMappingFirstKey, - FlowMappingKey, - FlowMappingValue, - FlowMappingEmptyValue - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core +{ + internal enum ParserState + { + StreamStart, + StreamEnd, + ImplicitDocumentStart, + DocumentStart, + DocumentContent, + DocumentEnd, + BlockNode, + BlockNodeOrIndentlessSequence, + FlowNode, + BlockSequenceFirstEntry, + BlockSequenceEntry, + IndentlessSequenceEntry, + BlockMappingFirstKey, + BlockMappingKey, + BlockMappingValue, + FlowSequenceFirstEntry, + FlowSequenceEntry, + FlowSequenceEntryMappingKey, + FlowSequenceEntryMappingValue, + FlowSequenceEntryMappingEnd, + FlowMappingFirstKey, + FlowMappingKey, + FlowMappingValue, + FlowMappingEmptyValue + } +} diff --git a/YamlDotNet/Core/RecursionLevel.cs b/YamlDotNet/Core/RecursionLevel.cs index b565ce2b8..e255bd5f5 100644 --- a/YamlDotNet/Core/RecursionLevel.cs +++ b/YamlDotNet/Core/RecursionLevel.cs @@ -1,80 +1,80 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - /// - /// Keeps track of the recursion level, - /// and throws - /// whenever is reached. - /// - internal sealed class RecursionLevel - { - private int current; - public int Maximum { get; } - - public RecursionLevel(int maximum) - { - Maximum = maximum; - } - - /// - /// Increments the recursion level, - /// and throws - /// if is reached. - /// - public void Increment() - { - if (!TryIncrement()) - { - throw new MaximumRecursionLevelReachedException("Maximum level of recursion reached"); - } - } - - /// - /// Increments the recursion level, - /// and returns whether is still less than . - /// - public bool TryIncrement() - { - if (current < Maximum) - { - ++current; - return true; - } - return false; - } - - /// - /// Decrements the recursion level. - /// - public void Decrement() - { - if (current == 0) - { - throw new InvalidOperationException("Attempted to decrement RecursionLevel to a negative value"); - } - --current; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + /// + /// Keeps track of the recursion level, + /// and throws + /// whenever is reached. + /// + internal sealed class RecursionLevel + { + private int current; + public int Maximum { get; } + + public RecursionLevel(int maximum) + { + Maximum = maximum; + } + + /// + /// Increments the recursion level, + /// and throws + /// if is reached. + /// + public void Increment() + { + if (!TryIncrement()) + { + throw new MaximumRecursionLevelReachedException("Maximum level of recursion reached"); + } + } + + /// + /// Increments the recursion level, + /// and returns whether is still less than . + /// + public bool TryIncrement() + { + if (current < Maximum) + { + ++current; + return true; + } + return false; + } + + /// + /// Decrements the recursion level. + /// + public void Decrement() + { + if (current == 0) + { + throw new InvalidOperationException("Attempted to decrement RecursionLevel to a negative value"); + } + --current; + } + } +} diff --git a/YamlDotNet/Core/ScalarStyle.cs b/YamlDotNet/Core/ScalarStyle.cs index 240b30b71..6cef41421 100644 --- a/YamlDotNet/Core/ScalarStyle.cs +++ b/YamlDotNet/Core/ScalarStyle.cs @@ -1,59 +1,59 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core -{ - /// - /// Specifies the style of a YAML scalar. - /// - public enum ScalarStyle - { - /// - /// Let the emitter choose the style. - /// - Any, - - /// - /// The plain scalar style. - /// - Plain, - - /// - /// The single-quoted scalar style. - /// - SingleQuoted, - - /// - /// The double-quoted scalar style. - /// - DoubleQuoted, - - /// - /// The literal scalar style. - /// - Literal, - - /// - /// The folded scalar style. - /// - Folded, - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core +{ + /// + /// Specifies the style of a YAML scalar. + /// + public enum ScalarStyle + { + /// + /// Let the emitter choose the style. + /// + Any, + + /// + /// The plain scalar style. + /// + Plain, + + /// + /// The single-quoted scalar style. + /// + SingleQuoted, + + /// + /// The double-quoted scalar style. + /// + DoubleQuoted, + + /// + /// The literal scalar style. + /// + Literal, + + /// + /// The folded scalar style. + /// + Folded, + } +} diff --git a/YamlDotNet/Core/Scanner.cs b/YamlDotNet/Core/Scanner.cs index 1cd133306..d89a6b9e0 100644 --- a/YamlDotNet/Core/Scanner.cs +++ b/YamlDotNet/Core/Scanner.cs @@ -1,2655 +1,2655 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; -using YamlDotNet.Core.Tokens; -using YamlDotNet.Helpers; - -namespace YamlDotNet.Core -{ - /// - /// Converts a sequence of characters into a sequence of YAML tokens. - /// - public class Scanner : IScanner - { - private const int MaxVersionNumberLength = 9; - - private static readonly SortedDictionary SimpleEscapeCodes = new SortedDictionary - { - { '0', '\0' }, - { 'a', '\x07' }, - { 'b', '\x08' }, - { 't', '\x09' }, - { '\t', '\x09' }, - { 'n', '\x0A' }, - { 'v', '\x0B' }, - { 'f', '\x0C' }, - { 'r', '\x0D' }, - { 'e', '\x1B' }, - { ' ', '\x20' }, - { '"', '"' }, - { '\\', '\\' }, - { '/', '/' }, - { 'N', '\x85' }, - { '_', '\xA0' }, - { 'L', '\x2028' }, - { 'P', '\x2029' } - }; - - private readonly Stack indents = new Stack(); - private readonly InsertionQueue tokens = new InsertionQueue(); - private readonly Stack simpleKeys = new Stack(); - private readonly CharacterAnalyzer analyzer; - - private readonly Cursor cursor; - private bool streamStartProduced; - private bool streamEndProduced; - private bool plainScalarFollowedByComment; - private int flowSequenceStartLine; - private bool flowCollectionFetched = false; - private bool startFlowCollectionFetched = false; - private int indent = -1; - private bool flowScalarFetched; - private bool simpleKeyAllowed; - private int flowLevel; - private int tokensParsed; - private bool tokenAvailable; - private Token? previous; - private Anchor? previousAnchor; - private Scalar? lastScalar = null; - - private bool IsDocumentStart() => - !analyzer.EndOfInput && - cursor.LineOffset == 0 && - analyzer.Check('-', 0) && - analyzer.Check('-', 1) && - analyzer.Check('-', 2) && - analyzer.IsWhiteBreakOrZero(3); - - private bool IsDocumentEnd() => - !analyzer.EndOfInput && - cursor.LineOffset == 0 && - analyzer.Check('.', 0) && - analyzer.Check('.', 1) && - analyzer.Check('.', 2) && - analyzer.IsWhiteBreakOrZero(3); - - private bool IsDocumentIndicator() => IsDocumentStart() || IsDocumentEnd(); - - public bool SkipComments - { - get; private set; - } - - /// - /// Gets the current token. - /// - public Token? Current - { - get; private set; - } - - /// - /// Initializes a new instance of the class. - /// - /// The input. - /// Indicates whether comments should be ignored - public Scanner(TextReader input, bool skipComments = true) - { - analyzer = new CharacterAnalyzer(new LookAheadBuffer(input, 1024)); - cursor = new Cursor(); - SkipComments = skipComments; - } - - /// - /// Gets the current position inside the input stream. - /// - /// The current position. - public Mark CurrentPosition - { - get - { - return cursor.Mark(); - } - } - - /// - /// Moves to the next token. - /// - /// - public bool MoveNext() - { - if (Current != null) - { - ConsumeCurrent(); - } - - return MoveNextWithoutConsuming(); - } - - public bool MoveNextWithoutConsuming() - { - if (!tokenAvailable && !streamEndProduced) - { - FetchMoreTokens(); - } - if (tokens.Count > 0) - { - Current = tokens.Dequeue(); - tokenAvailable = false; - return true; - } - else - { - Current = null; - return false; - } - } - - /// - /// Consumes the current token and increments the parsed token count - /// - public void ConsumeCurrent() - { - ++tokensParsed; - tokenAvailable = false; - previous = Current; - Current = null; - } - - private char ReadCurrentCharacter() - { - var currentCharacter = analyzer.Peek(0); - Skip(); - return currentCharacter; - } - - private char ReadLine() - { - if (analyzer.Check("\r\n\x85")) // CR LF -> LF --- CR|LF|NEL -> LF - { - SkipLine(); - return '\n'; - } - - var nextChar = analyzer.Peek(0); // LS|PS -> LS|PS - SkipLine(); - return nextChar; - } - - private void FetchMoreTokens() - { - // While we need more tokens to fetch, do it. - - while (true) - { - // Check if we really need to fetch more tokens. - - var needsMoreTokens = false; - - if (tokens.Count == 0) - { - // Queue is empty. - - needsMoreTokens = true; - } - else - { - // Check if any potential simple key may occupy the head position. - - foreach (var simpleKey in simpleKeys) - { - if (simpleKey.IsPossible && simpleKey.TokenNumber == tokensParsed) - { - needsMoreTokens = true; - break; - } - } - } - - // We are finished. - if (!needsMoreTokens) - { - break; - } - - // Fetch the next token. - - FetchNextToken(); - } - tokenAvailable = true; - } - - private static bool StartsWith(StringBuilder what, char start) - { - return what.Length > 0 && what[0] == start; - } - - /// - /// Check the list of potential simple keys and remove the positions that - /// cannot contain simple keys anymore. - /// - - private void StaleSimpleKeys() - { - // Check for a potential simple key for each flow level. - - foreach (var key in simpleKeys) - { - - // The specification requires that a simple key - - // - is limited to a single line, - // - is shorter than 1024 characters. - - - if (key.IsPossible && (key.Line < cursor.Line || key.Index + 1024 < cursor.Index)) - { - - // Check if the potential simple key to be removed is required. - - if (key.IsRequired) - { - var mark = cursor.Mark(); - tokens.Enqueue(new Error("While scanning a simple key, could not find expected ':'.", mark, mark)); - } - - key.MarkAsImpossible(); - } - } - } - - private void FetchNextToken() - { - // Check if we just started scanning. Fetch STREAM-START then. - if (!streamStartProduced) - { - FetchStreamStart(); - return; - } - - // Eat whitespaces and comments until we reach the next token. - - ScanToNextToken(); - - // Remove obsolete potential simple keys. - - StaleSimpleKeys(); - - // Check the indentation level against the current column. - - UnrollIndent(cursor.LineOffset); - - - // Ensure that the buffer contains at least 4 characters. 4 is the length - // of the longest indicators ('--- ' and '... '). - - - analyzer.Buffer.Cache(4); - - // Is it the end of the stream? - - if (analyzer.Buffer.EndOfInput) - { - lastScalar = null; - FetchStreamEnd(); - } - - // Is it a directive? - - if (cursor.LineOffset == 0 && analyzer.Check('%')) - { - lastScalar = null; - FetchDirective(); - return; - } - - // Is it the document start indicator? - - if (IsDocumentStart()) - { - lastScalar = null; - FetchDocumentIndicator(true); - return; - } - - // Is it the document end indicator? - - if (IsDocumentEnd()) - { - lastScalar = null; - FetchDocumentIndicator(false); - return; - } - - // Is it the flow sequence start indicator? - - if (analyzer.Check('[')) - { - lastScalar = null; - FetchFlowCollectionStart(true); - return; - } - - // Is it the flow mapping start indicator? - - if (analyzer.Check('{')) - { - lastScalar = null; - FetchFlowCollectionStart(false); - return; - } - - // Is it the flow sequence end indicator? - - if (analyzer.Check(']')) - { - lastScalar = null; - FetchFlowCollectionEnd(true); - return; - } - - // Is it the flow mapping end indicator? - - if (analyzer.Check('}')) - { - lastScalar = null; - FetchFlowCollectionEnd(false); - return; - } - - // Is it the flow entry indicator? - - if (analyzer.Check(',')) - { - lastScalar = null; - FetchFlowEntry(); - return; - } - - // Is it the block entry indicator? - - if (analyzer.Check('-')) - { - if (analyzer.IsWhiteBreakOrZero(1)) - { - FetchBlockEntry(); - return; - } - else if (analyzer.Check(",[]{}", 1)) - { - tokens.Enqueue(new Error("Invalid key indicator format.", cursor.Mark(), cursor.Mark())); - } - } - - // Is it the key indicator? - - if (analyzer.Check('?') && - (flowLevel > 0 || analyzer.IsWhiteBreakOrZero(1))) - { - if (analyzer.IsWhiteBreakOrZero(1)) - { - FetchKey(); - return; - } - } - - // Is it the value indicator? - if (analyzer.Check(':') && - (flowLevel > 0 || analyzer.IsWhiteBreakOrZero(1)) && - !(simpleKeyAllowed && flowLevel > 0) && - !(flowScalarFetched && analyzer.Check(':', 1))) - { - if (analyzer.IsWhiteBreakOrZero(1) || analyzer.Check(',', 1) || flowScalarFetched || flowCollectionFetched || startFlowCollectionFetched) - { - if (lastScalar != null) - { - lastScalar.IsKey = true; - lastScalar = null; - } - - FetchValue(); - return; - } - } - - // Is it an alias? - - if (analyzer.Check('*')) - { - FetchAnchor(true); - return; - } - - // Is it an anchor? - - if (analyzer.Check('&')) - { - FetchAnchor(false); - return; - } - - // Is it a tag? - - if (analyzer.Check('!')) - { - FetchTag(); - return; - } - - // Is it a literal scalar? - - if (analyzer.Check('|') && flowLevel == 0) - { - FetchBlockScalar(true); - return; - } - - // Is it a folded scalar? - - if (analyzer.Check('>') && flowLevel == 0) - { - FetchBlockScalar(false); - return; - } - - // Is it a single-quoted scalar? - - if (analyzer.Check('\'')) - { - FetchFlowScalar(true); - return; - } - - // Is it a double-quoted scalar? - - if (analyzer.Check('"')) - { - FetchFlowScalar(false); - return; - } - - - // Is it a plain scalar? - - // A plain scalar may start with any non-blank characters except - - // '-', '?', ':', ',', '[', ']', '{', '}', - // '#', '&', '*', '!', '|', '>', '\'', '\"', - // '%', '@', '`'. - - // In the block context (and, for the '-' indicator, in the flow context - // too), it may also start with the characters - - // '-', '?', ':' - - // if it is followed by a non-space character. - - // The last rule is more restrictive than the specification requires. - - - var isInvalidPlainScalarCharacter = analyzer.IsWhiteBreakOrZero() || analyzer.Check("-?:,[]{}#&*!|>'\"%@`"); - - var isPlainScalar = - !isInvalidPlainScalarCharacter || - (analyzer.Check('-') && !analyzer.IsWhite(1)) || - (analyzer.Check("?:") && !analyzer.IsWhiteBreakOrZero(1)) || - (simpleKeyAllowed && flowLevel > 0); - - if (isPlainScalar) - { - if (plainScalarFollowedByComment) - { - var startMark = cursor.Mark(); - tokens.Enqueue(new Error("While scanning plain scalar, found a comment between adjacent scalars.", startMark, startMark)); - } - - if (flowScalarFetched || flowCollectionFetched && !startFlowCollectionFetched) - { - if (analyzer.Check(':')) - { - Skip(); - } - } - - flowScalarFetched = false; - flowCollectionFetched = false; - startFlowCollectionFetched = false; - plainScalarFollowedByComment = false; - - FetchPlainScalar(); - return; - } - - if (simpleKeyAllowed && indent >= cursor.LineOffset && analyzer.IsTab()) - { - throw new SyntaxErrorException("While scanning a mapping, found invalid tab as indentation."); - } - - if (analyzer.IsWhiteBreakOrZero()) - { - Skip(); - return; - } - - // If we don't determine the token type so far, it is an error. - var start = cursor.Mark(); - Skip(); - var end = cursor.Mark(); - - throw new SyntaxErrorException(start, end, "While scanning for the next token, found character that cannot start any token."); - } - - private bool CheckWhiteSpace() - { - return analyzer.Check(' ') || ((flowLevel > 0 || !simpleKeyAllowed) && analyzer.Check('\t')); - } - - private void Skip() - { - cursor.Skip(); - analyzer.Buffer.Skip(1); - } - - private void SkipLine() - { - if (analyzer.IsCrLf()) - { - cursor.SkipLineByOffset(2); - analyzer.Buffer.Skip(2); - } - else if (analyzer.IsBreak()) - { - cursor.SkipLineByOffset(1); - analyzer.Buffer.Skip(1); - } - else if (!analyzer.IsZero()) - { - throw new InvalidOperationException("Not at a break."); - } - } - - private void ScanToNextToken() - { - // Until the next token is not find. - - while (true) - { - - // Eat whitespaces. - - // Tabs are allowed: - - // - in the flow context; - // - in the block context, but not at the beginning of the line or - // after '-', '?', or ':' (complex value). - - - while (CheckWhiteSpace()) - { - Skip(); - } - - ProcessComment(); - - // If it is a line break, eat it. - - if (analyzer.IsBreak()) - { - SkipLine(); - - // In the block context, a new line may start a simple key. - - if (flowLevel == 0) - { - simpleKeyAllowed = true; - } - } - else - { - // We have find a token. - - break; - } - } - } - - private void ProcessComment() - { - if (analyzer.Check('#')) - { - var start = cursor.Mark(); - - // Eat '#' - Skip(); - - // Eat leading whitespace - while (analyzer.IsSpace()) - { - Skip(); - } - - using var textBuilder = StringBuilderPool.Rent(); - var text = textBuilder.Builder; - while (!analyzer.IsBreakOrZero()) - { - text.Append(ReadCurrentCharacter()); - } - - if (!SkipComments) - { - var isInline = previous != null - && previous.End.Line == start.Line - && previous.End.Column != 1 - && !(previous is StreamStart); - - tokens.Enqueue(new Comment(text.ToString(), isInline, start, cursor.Mark())); - } - } - } - - private void FetchStreamStart() - { - // Initialize the simple key stack. - - simpleKeys.Push(new SimpleKey()); - - // A simple key is allowed at the beginning of the stream. - - simpleKeyAllowed = true; - - // We have started. - - streamStartProduced = true; - - // Create the STREAM-START token and append it to the queue. - - var mark = cursor.Mark(); - tokens.Enqueue(new StreamStart(mark, mark)); - } - - /// - /// Pop indentation levels from the indents stack until the current level - /// becomes less or equal to the column. For each indentation level, append - /// the BLOCK-END token. - /// - - private void UnrollIndent(int column) - { - // In the flow context, do nothing. - - if (flowLevel != 0) - { - return; - } - - // Loop through the indentation levels in the stack. - - while (indent > column) - { - // Create a token and append it to the queue. - - var mark = cursor.Mark(); - tokens.Enqueue(new BlockEnd(mark, mark)); - - // Pop the indentation level. - - indent = indents.Pop(); - } - } - - /// - /// Produce the STREAM-END token and shut down the scanner. - /// - private void FetchStreamEnd() - { - cursor.ForceSkipLineAfterNonBreak(); - - // Reset the indentation level. - - UnrollIndent(-1); - - // Reset simple keys. - - RemoveSimpleKey(); - - simpleKeyAllowed = false; - - // Create the STREAM-END token and append it to the queue. - - streamEndProduced = true; - var mark = cursor.Mark(); - tokens.Enqueue(new StreamEnd(mark, mark)); - } - - private void FetchDirective() - { - // Reset the indentation level. - - UnrollIndent(-1); - - // Reset simple keys. - - RemoveSimpleKey(); - - simpleKeyAllowed = false; - - // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. - - var token = ScanDirective(); - - // Append the token to the queue. - if (token != null) - { - tokens.Enqueue(token); - } - } - - /// - /// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. - /// - /// Scope: - /// %YAML 1.1 # a comment \n - /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// %TAG !yaml! tag:yaml.org,2002: \n - /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// - private Token? ScanDirective() - { - // Eat '%'. - - var start = cursor.Mark(); - - Skip(); - - // Scan the directive name. - - var name = ScanDirectiveName(start); - - // Is it a YAML directive? - - Token directive; - switch (name) - { - case "YAML": - if (previous is DocumentStart || previous is StreamStart || previous is DocumentEnd) - { - directive = ScanVersionDirectiveValue(start); - } - else - { - throw new SemanticErrorException(start, cursor.Mark(), "While scanning a version directive, did not find preceding ."); - } - break; - - case "TAG": - directive = ScanTagDirectiveValue(start); - break; - - default: - // warning: skipping reserved directive line - while (!analyzer.EndOfInput && !analyzer.Check('#') && !analyzer.IsBreak()) - { - Skip(); - } - return null; - } - - // Eat the rest of the line including any comments. - - while (analyzer.IsWhite()) - { - Skip(); - } - - ProcessComment(); - - // Check if we are at the end of the line. - - if (!analyzer.IsBreakOrZero()) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a directive, did not find expected comment or line break."); - } - - // Eat a line break. - - if (analyzer.IsBreak()) - { - SkipLine(); - } - - return directive; - } - - /// - /// Produce the DOCUMENT-START or DOCUMENT-END token. - /// - - private void FetchDocumentIndicator(bool isStartToken) - { - // Reset the indentation level. - - UnrollIndent(-1); - - // Reset simple keys. - - RemoveSimpleKey(); - - simpleKeyAllowed = false; - - // Consume the token. - - var start = cursor.Mark(); - - Skip(); - Skip(); - Skip(); - - if (isStartToken) - { - tokens.Enqueue(new DocumentStart(start, cursor.Mark())); - } - else - { - Token? errorToken = null; - while (!analyzer.EndOfInput && !analyzer.IsBreak() && !analyzer.Check('#')) - { - if (!analyzer.IsWhite()) - { - errorToken = new Error("While scanning a document end, found invalid content after '...' marker.", start, cursor.Mark()); - break; - } - Skip(); - } - tokens.Enqueue(new DocumentEnd(start, start)); - if (errorToken != null) - { - tokens.Enqueue(errorToken); - } - } - } - - /// - /// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. - /// - - private void FetchFlowCollectionStart(bool isSequenceToken) - { - // The indicators '[' and '{' may start a simple key. - - SaveSimpleKey(); - - // Increase the flow level. - - IncreaseFlowLevel(); - - // A simple key may follow the indicators '[' and '{'. - - simpleKeyAllowed = true; - - // Consume the token. - - var start = cursor.Mark(); - Skip(); - - // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. - - Token token; - if (isSequenceToken) - { - token = new FlowSequenceStart(start, start); - flowSequenceStartLine = token.Start.Line; - } - else - { - token = new FlowMappingStart(start, start); - } - - tokens.Enqueue(token); - startFlowCollectionFetched = true; - } - - /// - /// Increase the flow level and resize the simple key list if needed. - /// - - private void IncreaseFlowLevel() - { - // Reset the simple key on the next level. - - simpleKeys.Push(new SimpleKey()); - - // Increase the flow level. - - ++flowLevel; - } - - /// - /// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. - /// - - private void FetchFlowCollectionEnd(bool isSequenceToken) - { - // Reset any potential simple key on the current flow level. - - RemoveSimpleKey(); - - // Decrease the flow level. - - DecreaseFlowLevel(); - - // No simple keys after the indicators ']' and '}'. - - simpleKeyAllowed = false; - - // Consume the token. - - var start = cursor.Mark(); - Skip(); - - Token? token, errorToken = null; - if (isSequenceToken) - { - if (analyzer.Check('#')) - { - errorToken = new Error("While scanning a flow sequence end, found invalid comment after ']'.", start, start); - } - - token = new FlowSequenceEnd(start, start); - } - else - { - token = new FlowMappingEnd(start, start); - } - - tokens.Enqueue(token); - if (errorToken != null) - { - tokens.Enqueue(errorToken); - } - - flowCollectionFetched = true; - } - - /// - /// Decrease the flow level. - /// - - private void DecreaseFlowLevel() - { - // flowLevel could be zero in case of malformed YAML. - // Since this is handled elsewhere, just ignore it. - if (flowLevel > 0) - { - --flowLevel; - simpleKeys.Pop(); - } - } - - /// - /// Produce the FLOW-ENTRY token. - /// - - private void FetchFlowEntry() - { - // Reset any potential simple keys on the current flow level. - - RemoveSimpleKey(); - - // Simple keys are allowed after ','. - - simpleKeyAllowed = true; - - // Consume the token. - - var start = cursor.Mark(); - Skip(); - - var end = cursor.Mark(); - if (analyzer.Check('#')) - { - tokens.Enqueue(new Error("While scanning a flow entry, found invalid comment after comma.", start, end)); - return; - } - - // Create the FLOW-ENTRY token and append it to the queue. - - tokens.Enqueue(new FlowEntry(start, end)); - } - - /// - /// Produce the BLOCK-ENTRY token. - /// - - private void FetchBlockEntry() - { - // Check if the scanner is in the block context. - - if (flowLevel == 0) - { - // Check if we are allowed to start a new entry. - - if (!simpleKeyAllowed) - { - if (previousAnchor != null) - { - if (previousAnchor.End.Line == cursor.Line) - { - throw new SemanticErrorException(previousAnchor.Start, previousAnchor.End, "Anchor before sequence entry on same line is not allowed."); - } - } - var mark = cursor.Mark(); - tokens.Enqueue(new Error("Block sequence entries are not allowed in this context.", mark, mark)); - } - - // Add the BLOCK-SEQUENCE-START token if needed. - RollIndent(cursor.LineOffset, -1, true, cursor.Mark()); - } - else - { - - // It is an error for the '-' indicator to occur in the flow context, - // but we let the Parser detect and report about it because the Parser - // is able to point to the context. - - } - - // Reset any potential simple keys on the current flow level. - - RemoveSimpleKey(); - - // Simple keys are allowed after '-'. - - simpleKeyAllowed = true; - - // Consume the token. - - var start = cursor.Mark(); - Skip(); - - // Create the BLOCK-ENTRY token and append it to the queue. - - tokens.Enqueue(new BlockEntry(start, cursor.Mark())); - } - - /// - /// Produce the KEY token. - /// - - private void FetchKey() - { - // In the block context, additional checks are required. - - if (flowLevel == 0) - { - // Check if we are allowed to start a new key (not necessary simple). - - if (!simpleKeyAllowed) - { - var mark = cursor.Mark(); - throw new SyntaxErrorException(mark, mark, "Mapping keys are not allowed in this context."); - } - - // Add the BLOCK-MAPPING-START token if needed. - - RollIndent(cursor.LineOffset, -1, false, cursor.Mark()); - } - - // Reset any potential simple keys on the current flow level. - - RemoveSimpleKey(); - - // Simple keys are allowed after '?' in the block context. - - simpleKeyAllowed = flowLevel == 0; - - // Consume the token. - - var start = cursor.Mark(); - Skip(); - - // Create the KEY token and append it to the queue. - - tokens.Enqueue(new Key(start, cursor.Mark())); - } - - /// - /// Produce the VALUE token. - /// - - private void FetchValue() - { - var simpleKey = simpleKeys.Peek(); - - // Have we find a simple key? - - if (simpleKey.IsPossible) - { - // Create the KEY token and insert it into the queue. - - tokens.Insert(simpleKey.TokenNumber - tokensParsed, new Key(simpleKey.Mark, simpleKey.Mark)); - - // In the block context, we may need to add the BLOCK-MAPPING-START token. - - RollIndent(simpleKey.LineOffset, simpleKey.TokenNumber, false, simpleKey.Mark); - - // Remove the simple key. - - simpleKey.MarkAsImpossible(); - - // A simple key cannot follow another simple key. - - simpleKeyAllowed = false; - } - else - { - // The ':' indicator follows a complex key. - - // Simple keys after ':' are allowed in the block context. - - var localSimpleKeyAllowed = flowLevel == 0; - - // In the block context, extra checks are required. - - if (localSimpleKeyAllowed) - { - // Check if we are allowed to start a complex value. - - if (!simpleKeyAllowed) - { - var mark = cursor.Mark(); - tokens.Enqueue(new Error("Mapping values are not allowed in this context.", mark, mark)); - return; - } - - // Add the BLOCK-MAPPING-START token if needed. - - RollIndent(cursor.LineOffset, -1, false, cursor.Mark()); - - // Check if we are dealing with empty key. - - if (cursor.LineOffset == 0 && simpleKey.LineOffset == 0) - { - // Create the KEY token and insert it into the queue. - - tokens.Insert(tokens.Count, new Key(simpleKey.Mark, simpleKey.Mark)); - - // A simple key cannot follow another simple key. - - localSimpleKeyAllowed = false; - } - } - - simpleKeyAllowed = localSimpleKeyAllowed; - } - - // Consume the token. - - var start = cursor.Mark(); - Skip(); - - // Create the VALUE token and append it to the queue. - - tokens.Enqueue(new Value(start, cursor.Mark())); - } - - /// - /// Push the current indentation level to the stack and set the new level - /// the current column is greater than the indentation level. In this case, - /// append or insert the specified token into the token queue. - /// - private void RollIndent(int column, int number, bool isSequence, Mark position) - { - // In the flow context, do nothing. - - if (flowLevel > 0) - { - return; - } - - if (indent < column) - { - - // Push the current indentation level to the stack and set the new - // indentation level. - - - indents.Push(indent); - - indent = column; - - // Create a token and insert it into the queue. - - Token token; - if (isSequence) - { - token = new BlockSequenceStart(position, position); - } - else - { - token = new BlockMappingStart(position, position); - } - - if (number == -1) - { - tokens.Enqueue(token); - } - else - { - tokens.Insert(number - tokensParsed, token); - } - } - } - - /// - /// Produce the ALIAS or ANCHOR token. - /// - - private void FetchAnchor(bool isAlias) - { - // An anchor or an alias could be a simple key. - - SaveSimpleKey(); - - // A simple key cannot follow an anchor or an alias. - - simpleKeyAllowed = false; - - // Create the ALIAS or ANCHOR token and append it to the queue. - - tokens.Enqueue(ScanAnchor(isAlias)); - } - - private Token ScanAnchor(bool isAlias) - { - // Eat the indicator character. - - var start = cursor.Mark(); - - Skip(); - - var isAliasKey = false; - if (isAlias) - { - var key = simpleKeys.Peek(); - isAliasKey = key.IsRequired && key.IsPossible; - } - - // Consume the value. - // YAML 1.2 - section 6.9.2."Node Anchors" specifies disallowed characters - // in the anchor name as follows: - // '[', ']', '{', '}' and ',' - // ref: https://yaml.org/spec/1.2/spec.html#id2785586 - - using var valueBuilder = StringBuilderPool.Rent(); - var value = valueBuilder.Builder; - while (!analyzer.IsWhiteBreakOrZero()) - { - // Anchor: read all allowed characters - - // Alias: read all allowed characters except colon (':'); read colon when token is: - // * not used in key OR - // * used in key and colon is not last character - - if (!analyzer.Check("[]{},") && - !(isAliasKey && analyzer.Check(':') && analyzer.IsWhiteBreakOrZero(1))) - { - value.Append(ReadCurrentCharacter()); - } - else - { - break; - } - } - - // Check if length of the anchor is greater than 0 and it is followed by - // a whitespace character or one of the indicators: - - // '?', ':', ',', ']', '}', '%', '@', '`'. - - - if (value.Length == 0 || !(analyzer.IsWhiteBreakOrZero() || analyzer.Check("?:,]}%@`"))) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning an anchor or alias, found value containing disallowed: []{},"); - } - - // Create a token. - var name = new AnchorName(value.ToString()); - if (isAlias) - { - return new AnchorAlias(name, start, cursor.Mark()); - } - else - { - return previousAnchor = new Anchor(name, start, cursor.Mark()); - } - } - - /// - /// Produce the TAG token. - /// - - private void FetchTag() - { - // A tag could be a simple key. - - SaveSimpleKey(); - - // A simple key cannot follow a tag. - - simpleKeyAllowed = false; - - // Create the TAG token and append it to the queue. - - tokens.Enqueue(ScanTag()); - } - - /// - /// Scan a TAG token. - /// - - Token ScanTag() - { - var start = cursor.Mark(); - - // Check if the tag is in the canonical form. - - string handle; - string suffix; - - if (analyzer.Check('<', 1)) - { - // Set the handle to '' - - handle = string.Empty; - - // Eat '!<' - - Skip(); - Skip(); - - // Consume the tag value. - - suffix = ScanTagUri(null, start); - - // Check for '>' and eat it. - - if (!analyzer.Check('>')) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, did not find the expected '>'."); - } - - Skip(); - } - else - { - // The tag has either the '!suffix' or the '!handle!suffix' form. - - // First, try to scan a handle. - - var firstPart = ScanTagHandle(false, start); - - // Check if it is, indeed, handle. - - if (firstPart.Length > 1 && firstPart[0] == '!' && firstPart[firstPart.Length - 1] == '!') - { - handle = firstPart; - - // Scan the suffix now. - - suffix = ScanTagUri(null, start); - } - else - { - // It wasn't a handle after all. Scan the rest of the tag. - - suffix = ScanTagUri(firstPart, start); - - // Set the handle to '!'. - - handle = "!"; - - - // A special case: the '!' tag. Set the handle to '' and the - // suffix to '!'. - - - if (suffix.Length == 0) - { - suffix = handle; - handle = string.Empty; - } - } - } - - // Check the character which ends the tag. - - if (!analyzer.IsWhiteBreakOrZero() && !analyzer.Check(',')) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, did not find expected whitespace, comma or line break."); - } - - // Create a token. - - return new Tag(handle, suffix, start, cursor.Mark()); - } - - /// - /// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. - /// - - private void FetchBlockScalar(bool isLiteral) - { - // A block scalar can be a simple key - - SaveSimpleKey(); - - // A simple key may follow a block scalar. - - simpleKeyAllowed = true; - - // Create the SCALAR token and append it to the queue. - - tokens.Enqueue(ScanBlockScalar(isLiteral)); - } - - /// - /// Scan a block scalar. - /// - - Token ScanBlockScalar(bool isLiteral) - { - using var valueBuilder = StringBuilderPool.Rent(); - var value = valueBuilder.Builder; - - using var leadingBreakBuilder = StringBuilderPool.Rent(); - var leadingBreak = leadingBreakBuilder.Builder; - - using var trailingBreaksBuilder = StringBuilderPool.Rent(); - var trailingBreaks = trailingBreaksBuilder.Builder; - - var chomping = 0; - var increment = 0; - var currentIndent = 0; - var leadingBlank = false; - bool? isFirstLine = null; - - // Eat the indicator '|' or '>'. - - var start = cursor.Mark(); - - Skip(); - - // Check for a chomping indicator. - - if (analyzer.Check("+-")) - { - // Set the chomping method and eat the indicator. - - chomping = analyzer.Check('+') ? +1 : -1; - - Skip(); - - // Check for an indentation indicator. - - if (analyzer.IsDigit()) - { - // Check that the indentation is greater than 0. - - if (analyzer.Check('0')) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, found an indentation indicator equal to 0."); - } - - // Get the indentation level and eat the indicator. - - increment = analyzer.AsDigit(); - - Skip(); - } - } - - // Do the same as above, but in the opposite order. - - else if (analyzer.IsDigit()) - { - if (analyzer.Check('0')) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, found an indentation indicator equal to 0."); - } - - increment = analyzer.AsDigit(); - - Skip(); - - if (analyzer.Check("+-")) - { - chomping = analyzer.Check('+') ? +1 : -1; - - Skip(); - } - } - - // Check if there is a comment without whitespace after block scalar indicator (yaml-test-suite: X4QW). - - if (analyzer.Check('#')) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, found a comment without whtespace after '>' indicator."); - } - - // Eat whitespaces and comments to the end of the line. - - while (analyzer.IsWhite()) - { - Skip(); - } - - ProcessComment(); - - // Check if we are at the end of the line. - - if (!analyzer.IsBreakOrZero()) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, did not find expected comment or line break."); - } - - // Eat a line break. - - if (analyzer.IsBreak()) - { - SkipLine(); - if (!isFirstLine.HasValue) - { - isFirstLine = true; - } - else if (isFirstLine == true) - { - isFirstLine = false; - } - } - - var end = cursor.Mark(); - - // Set the indentation level if it was specified. - - if (increment != 0) - { - currentIndent = indent >= 0 ? indent + increment : increment; - } - - // Scan the leading line breaks and determine the indentation level if needed. - - currentIndent = ScanBlockScalarBreaks(currentIndent, trailingBreaks, isLiteral, ref end, ref isFirstLine); - isFirstLine = false; - - // Scan the block scalar content. - - while (cursor.LineOffset == currentIndent && !analyzer.IsZero() && !IsDocumentEnd()) - { - // We are at the beginning of a non-empty line. - - // Is it a trailing whitespace? - - var trailingBlank = analyzer.IsWhite(); - - // Check if we need to fold the leading line break. - - if (!isLiteral && StartsWith(leadingBreak, '\n') && !leadingBlank && !trailingBlank) - { - // Do we need to join the lines by space? - - if (trailingBreaks.Length == 0) - { - value.Append(' '); - } - - leadingBreak.Length = 0; - } - else - { - value.Append(leadingBreak.ToString()); - leadingBreak.Length = 0; - } - - // Append the remaining line breaks. - - value.Append(trailingBreaks.ToString()); - trailingBreaks.Length = 0; - - // Is it a leading whitespace? - - leadingBlank = analyzer.IsWhite(); - - // Consume the current line. - - while (!analyzer.IsBreakOrZero()) - { - value.Append(ReadCurrentCharacter()); - } - - // Consume the line break. - var lineBreak = ReadLine(); - if (lineBreak != '\0') - { - leadingBreak.Append(lineBreak); - } - - // Eat the following indentation spaces and line breaks. - - currentIndent = ScanBlockScalarBreaks(currentIndent, trailingBreaks, isLiteral, ref end, ref isFirstLine); - } - - // Chomp the tail. - - if (chomping != -1) - { - value.Append(leadingBreak); - } - if (chomping == 1) - { - value.Append(trailingBreaks); - } - - // Create a token. - - var style = isLiteral ? ScalarStyle.Literal : ScalarStyle.Folded; - return new Scalar(value.ToString(), style, start, end); - } - - /// - /// Scan indentation spaces and line breaks for a block scalar. Determine the - /// indentation level if needed. - /// - - private int ScanBlockScalarBreaks(int currentIndent, StringBuilder breaks, bool isLiteral, ref Mark end, ref bool? isFirstLine) - { - var maxIndent = 0; - var indentOfFirstLine = -1; - - end = cursor.Mark(); - - // Eat the indentation spaces and line breaks. - - while (true) - { - // Eat the indentation spaces. - - while ((currentIndent == 0 || cursor.LineOffset < currentIndent) && analyzer.IsSpace()) - { - Skip(); - } - - if (cursor.LineOffset > maxIndent) - { - maxIndent = cursor.LineOffset; - } - - // Have we find a non-empty line? - - if (!analyzer.IsBreak()) - { - if (isLiteral && isFirstLine == true) - { - var localIndent = cursor.LineOffset; - var i = 0; - while (!analyzer.IsBreak(i) && analyzer.IsSpace(i)) - { - ++i; - ++localIndent; - } - - if (analyzer.IsBreak(i) && localIndent > cursor.LineOffset) - { - isFirstLine = false; - indentOfFirstLine = localIndent; - } - } - break; - } - - if (isFirstLine == true) - { - isFirstLine = false; - indentOfFirstLine = cursor.LineOffset; - } - - // Consume the line break. - - breaks.Append(ReadLine()); - - end = cursor.Mark(); - } - - // Check if first line after literal is all spaces and count of spaces is more than "1 + currentIndent". - - if (isLiteral && indentOfFirstLine > 1 && currentIndent < indentOfFirstLine - 1) - { - // W9L4 - throw new SemanticErrorException(end, cursor.Mark(), "While scanning a literal block scalar, found extra spaces in first line."); - } - - if (!isLiteral && maxIndent > cursor.LineOffset && indentOfFirstLine > -1) - { - // S98Z - throw new SemanticErrorException(end, cursor.Mark(), "While scanning a literal block scalar, found more spaces in lines above first content line."); - } - - // Determine the indentation level if needed. - - if (currentIndent == 0 && (cursor.LineOffset > 0 || indent > -1)) - { - currentIndent = Math.Max(maxIndent, Math.Max(indent + 1, 1)); - } - - return currentIndent; - } - - /// - /// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. - /// - - private void FetchFlowScalar(bool isSingleQuoted) - { - // A plain scalar could be a simple key. - - SaveSimpleKey(); - - // A simple key cannot follow a flow scalar. - - simpleKeyAllowed = false; - - // Indicates the adjacent flow scalar that a prior flow scalar has been fetched. - - flowScalarFetched = true; - - // Create the SCALAR token and append it to the queue. - - tokens.Enqueue(ScanFlowScalar(isSingleQuoted)); - - // Check if there is a comment subsequently after double-quoted scalar without space. - - if (!isSingleQuoted && analyzer.Check('#')) - { - var start = cursor.Mark(); - tokens.Enqueue(new Error("While scanning a flow sequence end, found invalid comment after double-quoted scalar.", start, start)); - } - } - - /// - /// Scan a quoted scalar. - /// - - private Token ScanFlowScalar(bool isSingleQuoted) - { - // Eat the left quote. - - var start = cursor.Mark(); - - Skip(); - - // Consume the content of the quoted scalar. - - using var valueBuilder = StringBuilderPool.Rent(); - var value = valueBuilder.Builder; - - using var whitespacesBuilder = StringBuilderPool.Rent(); - var whitespaces = whitespacesBuilder.Builder; - - using var leadingBreakBuilder = StringBuilderPool.Rent(); - var leadingBreak = leadingBreakBuilder.Builder; - - using var trailingBreaksBuilder = StringBuilderPool.Rent(); - var trailingBreaks = trailingBreaksBuilder.Builder; - - var hasLeadingBlanks = false; - - while (true) - { - // Check that there are no document indicators at the beginning of the line. - - if (IsDocumentIndicator()) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, found unexpected document indicator."); - } - - // Check for EOF. - - if (analyzer.IsZero()) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, found unexpected end of stream."); - } - - if (hasLeadingBlanks && !isSingleQuoted && indent >= cursor.LineOffset) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a multi-line double-quoted scalar, found wrong indentation."); - } - - hasLeadingBlanks = false; - - // Consume non-blank characters. - - while (!analyzer.IsWhiteBreakOrZero()) - { - // Check for an escaped single quote. - - if (isSingleQuoted && analyzer.Check('\'', 0) && analyzer.Check('\'', 1)) - { - value.Append('\''); - Skip(); - Skip(); - } - - // Check for the right quote. - - else if (analyzer.Check(isSingleQuoted ? '\'' : '"')) - { - break; - } - - // Check for an escaped line break. - - else if (!isSingleQuoted && analyzer.Check('\\') && analyzer.IsBreak(1)) - { - Skip(); - SkipLine(); - hasLeadingBlanks = true; - break; - } - - // Check for an escape sequence. - - else if (!isSingleQuoted && analyzer.Check('\\')) - { - var codeLength = 0; - - // Check the escape character. - - var escapeCharacter = analyzer.Peek(1); - switch (escapeCharacter) - { - case 'x': - codeLength = 2; - break; - - case 'u': - codeLength = 4; - break; - - case 'U': - codeLength = 8; - break; - - default: - char unescapedCharacter; - if (SimpleEscapeCodes.TryGetValue(escapeCharacter, out unescapedCharacter)) - { - value.Append(unescapedCharacter); - } - else - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, found unknown escape character."); - } - break; - } - - Skip(); - Skip(); - - // Consume an arbitrary escape code. - - if (codeLength > 0) - { - var character = 0; - - // Scan the character value. - - for (var k = 0; k < codeLength; ++k) - { - if (!analyzer.IsHex(k)) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, did not find expected hexadecimal number."); - } - character = ((character << 4) + analyzer.AsHex(k)); - } - - // Check the value and write the character. - - if ((character >= 0xD800 && character <= 0xDFFF) || character > 0x10FFFF) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, found invalid Unicode character escape code."); - } - - value.Append(char.ConvertFromUtf32(character)); - - // Advance the pointer. - - for (var k = 0; k < codeLength; ++k) - { - Skip(); - } - } - } - else - { - // It is a non-escaped non-blank character. - - value.Append(ReadCurrentCharacter()); - } - } - - // Check if we are at the end of the scalar. - - if (analyzer.Check(isSingleQuoted ? '\'' : '"')) - { - break; - } - - // Consume blank characters. - - while (analyzer.IsWhite() || analyzer.IsBreak()) - { - if (analyzer.IsWhite()) - { - // Consume a space or a tab character. - - if (!hasLeadingBlanks) - { - whitespaces.Append(ReadCurrentCharacter()); - } - else - { - Skip(); - } - } - else - { - // Check if it is a first line break. - - if (!hasLeadingBlanks) - { - whitespaces.Length = 0; - leadingBreak.Append(ReadLine()); - hasLeadingBlanks = true; - } - else - { - trailingBreaks.Append(ReadLine()); - } - } - } - - // Join the whitespaces or fold line breaks. - - if (hasLeadingBlanks) - { - // Do we need to fold line breaks? - - if (StartsWith(leadingBreak, '\n')) - { - if (trailingBreaks.Length == 0) - { - value.Append(' '); - } - else - { - value.Append(trailingBreaks.ToString()); - } - } - else - { - value.Append(leadingBreak.ToString()); - value.Append(trailingBreaks.ToString()); - } - leadingBreak.Length = 0; - trailingBreaks.Length = 0; - } - else - { - value.Append(whitespaces.ToString()); - whitespaces.Length = 0; - } - } - - // Eat the right quote. - - Skip(); - - return new Scalar(value.ToString(), isSingleQuoted ? ScalarStyle.SingleQuoted : ScalarStyle.DoubleQuoted, start, cursor.Mark()); - } - - /// - /// Produce the SCALAR(...,plain) token. - /// - - private void FetchPlainScalar() - { - // A plain scalar could be a simple key. - - SaveSimpleKey(); - - // A simple key cannot follow a flow scalar. - - simpleKeyAllowed = false; - - // Create the SCALAR token and append it to the queue. - var isMultiline = false; - var scalar = ScanPlainScalar(ref isMultiline); - lastScalar = scalar; - if (isMultiline && analyzer.Check(':') && flowLevel == 0 && indent < cursor.LineOffset) - { - tokens.Enqueue(new Error("While scanning a multiline plain scalar, found invalid mapping.", cursor.Mark(), cursor.Mark())); - } - tokens.Enqueue(scalar); - } - - /// - /// Scan a plain scalar. - /// - - private Scalar ScanPlainScalar(ref bool isMultiline) - { - using var valueBuilder = StringBuilderPool.Rent(); - var value = valueBuilder.Builder; - - using var whitespacesBuilder = StringBuilderPool.Rent(); - var whitespaces = whitespacesBuilder.Builder; - - using var leadingBreakBuilder = StringBuilderPool.Rent(); - var leadingBreak = leadingBreakBuilder.Builder; - - using var trailingBreaksBuilder = StringBuilderPool.Rent(); - var trailingBreaks = trailingBreaksBuilder.Builder; - - var hasLeadingBlanks = false; - var currentIndent = indent + 1; - - var start = cursor.Mark(); - var end = start; - - var key = simpleKeys.Peek(); - - // Consume the content of the plain scalar. - - while (true) - { - // Check for a document indicator. - - if (IsDocumentIndicator()) - { - break; - } - - // Check for a comment. - - if (analyzer.Check('#')) - { - if (indent < 0 && flowLevel == 0) - { - plainScalarFollowedByComment = true; - } - break; - } - - var isAliasValue = analyzer.Check('*') && !(key.IsPossible && key.IsRequired); - - // Consume non-blank characters. - while (!analyzer.IsWhiteBreakOrZero()) - { - // Check for indicators that may end a plain scalar. - - if (analyzer.Check(':') && - !isAliasValue && - (analyzer.IsWhiteBreakOrZero(1) || - (flowLevel > 0 && analyzer.Check(',', 1))) || - (flowLevel > 0 && analyzer.Check(",[]{}"))) - { - if (flowLevel == 0 && !key.IsPossible) - { - tokens.Enqueue(new Error("While scanning a plain scalar value, found invalid mapping.", cursor.Mark(), cursor.Mark())); - } - break; - } - - // Check if we need to join whitespaces and breaks. - - if (hasLeadingBlanks || whitespaces.Length > 0) - { - if (hasLeadingBlanks) - { - // Do we need to fold line breaks? - - if (StartsWith(leadingBreak, '\n')) - { - if (trailingBreaks.Length == 0) - { - value.Append(' '); - } - else - { - value.Append(trailingBreaks); - } - } - else - { - value.Append(leadingBreak); - value.Append(trailingBreaks); - } - - leadingBreak.Length = 0; - trailingBreaks.Length = 0; - - hasLeadingBlanks = false; - } - else - { - value.Append(whitespaces); - whitespaces.Length = 0; - } - } - if (flowLevel > 0 && cursor.LineOffset < currentIndent) - { - throw new Exception(); - } - // Copy the character. - - value.Append(ReadCurrentCharacter()); - - end = cursor.Mark(); - } - - // Is it the end? - - if (!(analyzer.IsWhite() || analyzer.IsBreak())) - { - break; - } - - // Consume blank characters. - - while (analyzer.IsWhite() || analyzer.IsBreak()) - { - if (analyzer.IsWhite()) - { - // Check for tab character that abuse indentation. - - if (hasLeadingBlanks && cursor.LineOffset < currentIndent && analyzer.IsTab()) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a plain scalar, found a tab character that violate indentation."); - } - - // Consume a space or a tab character. - - if (!hasLeadingBlanks) - { - whitespaces.Append(ReadCurrentCharacter()); - } - else - { - Skip(); - } - } - else - { - isMultiline = true; - - // Check if it is a first line break. - - if (!hasLeadingBlanks) - { - whitespaces.Length = 0; - leadingBreak.Append(ReadLine()); - hasLeadingBlanks = true; - } - else - { - trailingBreaks.Append(ReadLine()); - } - } - } - - // Check indentation level. - - if (flowLevel == 0 && cursor.LineOffset < currentIndent) - { - break; - } - } - - // Note that we change the 'simple_key_allowed' flag. - - if (hasLeadingBlanks) - { - simpleKeyAllowed = true; - } - - // Create a token. - - return new Scalar(value.ToString(), ScalarStyle.Plain, start, end); - } - - - /// - /// Remove a potential simple key at the current flow level. - /// - - private void RemoveSimpleKey() - { - var key = simpleKeys.Peek(); - - if (key.IsPossible && key.IsRequired) - { - // If the key is required, it is an error. - - throw new SyntaxErrorException(key.Mark, key.Mark, "While scanning a simple key, could not find expected ':'."); - } - - // Remove the key from the stack. - - key.MarkAsImpossible(); - } - - /// - /// Scan the directive name. - /// - /// Scope: - /// %YAML 1.1 # a comment \n - /// ^^^^ - /// %TAG !yaml! tag:yaml.org,2002: \n - /// ^^^ - /// - private string ScanDirectiveName(in Mark start) - { - using var nameBuilder = StringBuilderPool.Rent(); - var name = nameBuilder.Builder; - - // Consume the directive name. - - while (analyzer.IsAlphaNumericDashOrUnderscore()) - { - name.Append(ReadCurrentCharacter()); - } - - // Check if the name is empty. - - if (name.Length == 0) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a directive, could not find expected directive name."); - } - - // Check for end of stream - - if (analyzer.EndOfInput) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a directive, found unexpected end of stream."); - } - - // Check for an blank character after the name. - - if (!analyzer.IsWhiteBreakOrZero()) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a directive, found unexpected non-alphabetical character."); - } - - return name.ToString(); - } - - private void SkipWhitespaces() - { - // Eat whitespaces. - - while (analyzer.IsWhite()) - { - Skip(); - } - } - - /// - /// Scan the value of VERSION-DIRECTIVE. - /// - /// Scope: - /// %YAML 1.1 # a comment \n - /// ^^^^^^ - /// - private Token ScanVersionDirectiveValue(in Mark start) - { - SkipWhitespaces(); - - // Consume the major version number. - - var major = ScanVersionDirectiveNumber(start); - - // Eat '.'. - - if (!analyzer.Check('.')) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %YAML directive, did not find expected digit or '.' character."); - } - - Skip(); - - // Consume the minor version number. - - var minor = ScanVersionDirectiveNumber(start); - - return new VersionDirective(new Version(major, minor), start, start); - } - - /// - /// Scan the value of a TAG-DIRECTIVE token. - /// - /// Scope: - /// %TAG !yaml! tag:yaml.org,2002: \n - /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// - private Token ScanTagDirectiveValue(in Mark start) - { - SkipWhitespaces(); - - // Scan a handle. - - var handle = ScanTagHandle(true, start); - - // Expect a whitespace. - - if (!analyzer.IsWhite()) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %TAG directive, did not find expected whitespace."); - } - - SkipWhitespaces(); - - // Scan a prefix. - - var prefix = ScanTagUri(null, start); - - // Expect a whitespace or line break. - - if (!analyzer.IsWhiteBreakOrZero()) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %TAG directive, did not find expected whitespace or line break."); - } - - return new TagDirective(handle, prefix, start, start); - } - - /// - /// Scan a tag. - /// - - private string ScanTagUri(string? head, Mark start) - { - using var tagBuilder = StringBuilderPool.Rent(); - var tag = tagBuilder.Builder; - - if (head != null && head.Length > 1) - { - tag.Append(head.Substring(1)); - } - - // Scan the tag. - - // The set of characters that may appear in URI is as follows: - - // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', - // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', - // '%'. - - - while (analyzer.IsAlphaNumericDashOrUnderscore() || analyzer.Check(";/?:@&=+$.!~*'()[]%") || - (analyzer.Check(',') && !analyzer.IsBreak(1))) - { - // Check if it is a URI-escape sequence. - - if (analyzer.Check('%')) - { - tag.Append(ScanUriEscapes(start)); - } - else if (analyzer.Check('+')) - { - tag.Append(' '); - Skip(); - } - else - { - tag.Append(ReadCurrentCharacter()); - } - } - - // Check if the tag is non-empty. - - if (tag.Length == 0) - { - return string.Empty; - } - - var result = tag.ToString(); - if (result.EndsWith(",")) - { - throw new SyntaxErrorException(cursor.Mark(), cursor.Mark(), "Unexpected comma at end of tag"); - } - - return result; - } - - private static readonly byte[] EmptyBytes = new byte[0]; - - /// - /// Decode an URI-escape sequence corresponding to a single UTF-8 character. - /// - - private string ScanUriEscapes(in Mark start) - { - // Decode the required number of characters. - - var charBytes = EmptyBytes; - var nextInsertionIndex = 0; - var width = 0; - do - { - // Check for a URI-escaped octet. - - if (!(analyzer.Check('%') && analyzer.IsHex(1) && analyzer.IsHex(2))) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, did not find URI escaped octet."); - } - - // Get the octet. - - var octet = (analyzer.AsHex(1) << 4) + analyzer.AsHex(2); - - // If it is the leading octet, determine the length of the UTF-8 sequence. - - if (width == 0) - { - width = (octet & 0x80) == 0x00 ? 1 : - (octet & 0xE0) == 0xC0 ? 2 : - (octet & 0xF0) == 0xE0 ? 3 : - (octet & 0xF8) == 0xF0 ? 4 : 0; - - if (width == 0) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, found an incorrect leading UTF-8 octet."); - } - - charBytes = new byte[width]; - } - else - { - // Check if the trailing octet is correct. - - if ((octet & 0xC0) != 0x80) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, found an incorrect trailing UTF-8 octet."); - } - } - - // Copy the octet and move the pointers. - - charBytes[nextInsertionIndex++] = (byte)octet; - - Skip(); - Skip(); - Skip(); - } - while (--width > 0); - - var result = Encoding.UTF8.GetString(charBytes, 0, nextInsertionIndex); - - if (result.Length == 0 || result.Length > 2) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, found an incorrect UTF-8 sequence."); - } - - return result; - } - - /// - /// Scan a tag handle. - /// - - private string ScanTagHandle(bool isDirective, Mark start) - { - - // Check the initial '!' character. - - if (!analyzer.Check('!')) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, did not find expected '!'."); - } - - // Copy the '!' character. - - using var tagHandleBuilder = StringBuilderPool.Rent(); - var tagHandle = tagHandleBuilder.Builder; - tagHandle.Append(ReadCurrentCharacter()); - - // Copy all subsequent alphabetical and numerical characters. - - while (analyzer.IsAlphaNumericDashOrUnderscore()) - { - tagHandle.Append(ReadCurrentCharacter()); - } - - // Check if the trailing character is '!' and copy it. - - if (analyzer.Check('!')) - { - tagHandle.Append(ReadCurrentCharacter()); - } - else - { - - // It's either the '!' tag or not really a tag handle. If it's a %TAG - // directive, it's an error. If it's a tag token, it must be a part of - // URI. - - - if (isDirective && (tagHandle.Length != 1 || tagHandle[0] != '!')) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag directive, did not find expected '!'."); - } - } - - return tagHandle.ToString(); - } - - /// - /// Scan the version number of VERSION-DIRECTIVE. - /// - /// Scope: - /// %YAML 1.1 # a comment \n - /// ^ - /// %YAML 1.1 # a comment \n - /// ^ - /// - private int ScanVersionDirectiveNumber(in Mark start) - { - var value = 0; - var length = 0; - - // Repeat while the next character is digit. - - while (analyzer.IsDigit()) - { - // Check if the number is too long. - - if (++length > MaxVersionNumberLength) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %YAML directive, found extremely long version number."); - } - - value = value * 10 + analyzer.AsDigit(); - - Skip(); - } - - // Check if the number was present. - - if (length == 0) - { - throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %YAML directive, did not find expected version number."); - } - - return value; - } - - /// - /// Check if a simple key may start at the current position and add it if - /// needed. - /// - - private void SaveSimpleKey() - { - - // A simple key is required at the current position if the scanner is in - // the block context and the current column coincides with the indentation - // level. - - - var isRequired = (flowLevel == 0 && indent == cursor.LineOffset); - - - // A simple key is required only when it is the first token in the current - // line. Therefore it is always allowed. But we add a check anyway. - - - Debug.Assert(simpleKeyAllowed || !isRequired, "Can't require a simple key and disallow it at the same time."); // Impossible. - - - // If the current position may start a simple key, save it. - - - if (simpleKeyAllowed) - { - var key = new SimpleKey(isRequired, tokensParsed + tokens.Count, cursor); - - RemoveSimpleKey(); - - simpleKeys.Pop(); - simpleKeys.Push(key); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using YamlDotNet.Core.Tokens; +using YamlDotNet.Helpers; + +namespace YamlDotNet.Core +{ + /// + /// Converts a sequence of characters into a sequence of YAML tokens. + /// + public class Scanner : IScanner + { + private const int MaxVersionNumberLength = 9; + + private static readonly SortedDictionary SimpleEscapeCodes = new SortedDictionary + { + { '0', '\0' }, + { 'a', '\x07' }, + { 'b', '\x08' }, + { 't', '\x09' }, + { '\t', '\x09' }, + { 'n', '\x0A' }, + { 'v', '\x0B' }, + { 'f', '\x0C' }, + { 'r', '\x0D' }, + { 'e', '\x1B' }, + { ' ', '\x20' }, + { '"', '"' }, + { '\\', '\\' }, + { '/', '/' }, + { 'N', '\x85' }, + { '_', '\xA0' }, + { 'L', '\x2028' }, + { 'P', '\x2029' } + }; + + private readonly Stack indents = new Stack(); + private readonly InsertionQueue tokens = new InsertionQueue(); + private readonly Stack simpleKeys = new Stack(); + private readonly CharacterAnalyzer analyzer; + + private readonly Cursor cursor; + private bool streamStartProduced; + private bool streamEndProduced; + private bool plainScalarFollowedByComment; + private int flowSequenceStartLine; + private bool flowCollectionFetched = false; + private bool startFlowCollectionFetched = false; + private int indent = -1; + private bool flowScalarFetched; + private bool simpleKeyAllowed; + private int flowLevel; + private int tokensParsed; + private bool tokenAvailable; + private Token? previous; + private Anchor? previousAnchor; + private Scalar? lastScalar = null; + + private bool IsDocumentStart() => + !analyzer.EndOfInput && + cursor.LineOffset == 0 && + analyzer.Check('-', 0) && + analyzer.Check('-', 1) && + analyzer.Check('-', 2) && + analyzer.IsWhiteBreakOrZero(3); + + private bool IsDocumentEnd() => + !analyzer.EndOfInput && + cursor.LineOffset == 0 && + analyzer.Check('.', 0) && + analyzer.Check('.', 1) && + analyzer.Check('.', 2) && + analyzer.IsWhiteBreakOrZero(3); + + private bool IsDocumentIndicator() => IsDocumentStart() || IsDocumentEnd(); + + public bool SkipComments + { + get; private set; + } + + /// + /// Gets the current token. + /// + public Token? Current + { + get; private set; + } + + /// + /// Initializes a new instance of the class. + /// + /// The input. + /// Indicates whether comments should be ignored + public Scanner(TextReader input, bool skipComments = true) + { + analyzer = new CharacterAnalyzer(new LookAheadBuffer(input, 1024)); + cursor = new Cursor(); + SkipComments = skipComments; + } + + /// + /// Gets the current position inside the input stream. + /// + /// The current position. + public Mark CurrentPosition + { + get + { + return cursor.Mark(); + } + } + + /// + /// Moves to the next token. + /// + /// + public bool MoveNext() + { + if (Current != null) + { + ConsumeCurrent(); + } + + return MoveNextWithoutConsuming(); + } + + public bool MoveNextWithoutConsuming() + { + if (!tokenAvailable && !streamEndProduced) + { + FetchMoreTokens(); + } + if (tokens.Count > 0) + { + Current = tokens.Dequeue(); + tokenAvailable = false; + return true; + } + else + { + Current = null; + return false; + } + } + + /// + /// Consumes the current token and increments the parsed token count + /// + public void ConsumeCurrent() + { + ++tokensParsed; + tokenAvailable = false; + previous = Current; + Current = null; + } + + private char ReadCurrentCharacter() + { + var currentCharacter = analyzer.Peek(0); + Skip(); + return currentCharacter; + } + + private char ReadLine() + { + if (analyzer.Check("\r\n\x85")) // CR LF -> LF --- CR|LF|NEL -> LF + { + SkipLine(); + return '\n'; + } + + var nextChar = analyzer.Peek(0); // LS|PS -> LS|PS + SkipLine(); + return nextChar; + } + + private void FetchMoreTokens() + { + // While we need more tokens to fetch, do it. + + while (true) + { + // Check if we really need to fetch more tokens. + + var needsMoreTokens = false; + + if (tokens.Count == 0) + { + // Queue is empty. + + needsMoreTokens = true; + } + else + { + // Check if any potential simple key may occupy the head position. + + foreach (var simpleKey in simpleKeys) + { + if (simpleKey.IsPossible && simpleKey.TokenNumber == tokensParsed) + { + needsMoreTokens = true; + break; + } + } + } + + // We are finished. + if (!needsMoreTokens) + { + break; + } + + // Fetch the next token. + + FetchNextToken(); + } + tokenAvailable = true; + } + + private static bool StartsWith(StringBuilder what, char start) + { + return what.Length > 0 && what[0] == start; + } + + /// + /// Check the list of potential simple keys and remove the positions that + /// cannot contain simple keys anymore. + /// + + private void StaleSimpleKeys() + { + // Check for a potential simple key for each flow level. + + foreach (var key in simpleKeys) + { + + // The specification requires that a simple key + + // - is limited to a single line, + // - is shorter than 1024 characters. + + + if (key.IsPossible && (key.Line < cursor.Line || key.Index + 1024 < cursor.Index)) + { + + // Check if the potential simple key to be removed is required. + + if (key.IsRequired) + { + var mark = cursor.Mark(); + tokens.Enqueue(new Error("While scanning a simple key, could not find expected ':'.", mark, mark)); + } + + key.MarkAsImpossible(); + } + } + } + + private void FetchNextToken() + { + // Check if we just started scanning. Fetch STREAM-START then. + if (!streamStartProduced) + { + FetchStreamStart(); + return; + } + + // Eat whitespaces and comments until we reach the next token. + + ScanToNextToken(); + + // Remove obsolete potential simple keys. + + StaleSimpleKeys(); + + // Check the indentation level against the current column. + + UnrollIndent(cursor.LineOffset); + + + // Ensure that the buffer contains at least 4 characters. 4 is the length + // of the longest indicators ('--- ' and '... '). + + + analyzer.Buffer.Cache(4); + + // Is it the end of the stream? + + if (analyzer.Buffer.EndOfInput) + { + lastScalar = null; + FetchStreamEnd(); + } + + // Is it a directive? + + if (cursor.LineOffset == 0 && analyzer.Check('%')) + { + lastScalar = null; + FetchDirective(); + return; + } + + // Is it the document start indicator? + + if (IsDocumentStart()) + { + lastScalar = null; + FetchDocumentIndicator(true); + return; + } + + // Is it the document end indicator? + + if (IsDocumentEnd()) + { + lastScalar = null; + FetchDocumentIndicator(false); + return; + } + + // Is it the flow sequence start indicator? + + if (analyzer.Check('[')) + { + lastScalar = null; + FetchFlowCollectionStart(true); + return; + } + + // Is it the flow mapping start indicator? + + if (analyzer.Check('{')) + { + lastScalar = null; + FetchFlowCollectionStart(false); + return; + } + + // Is it the flow sequence end indicator? + + if (analyzer.Check(']')) + { + lastScalar = null; + FetchFlowCollectionEnd(true); + return; + } + + // Is it the flow mapping end indicator? + + if (analyzer.Check('}')) + { + lastScalar = null; + FetchFlowCollectionEnd(false); + return; + } + + // Is it the flow entry indicator? + + if (analyzer.Check(',')) + { + lastScalar = null; + FetchFlowEntry(); + return; + } + + // Is it the block entry indicator? + + if (analyzer.Check('-')) + { + if (analyzer.IsWhiteBreakOrZero(1)) + { + FetchBlockEntry(); + return; + } + else if (analyzer.Check(",[]{}", 1)) + { + tokens.Enqueue(new Error("Invalid key indicator format.", cursor.Mark(), cursor.Mark())); + } + } + + // Is it the key indicator? + + if (analyzer.Check('?') && + (flowLevel > 0 || analyzer.IsWhiteBreakOrZero(1))) + { + if (analyzer.IsWhiteBreakOrZero(1)) + { + FetchKey(); + return; + } + } + + // Is it the value indicator? + if (analyzer.Check(':') && + (flowLevel > 0 || analyzer.IsWhiteBreakOrZero(1)) && + !(simpleKeyAllowed && flowLevel > 0) && + !(flowScalarFetched && analyzer.Check(':', 1))) + { + if (analyzer.IsWhiteBreakOrZero(1) || analyzer.Check(',', 1) || flowScalarFetched || flowCollectionFetched || startFlowCollectionFetched) + { + if (lastScalar != null) + { + lastScalar.IsKey = true; + lastScalar = null; + } + + FetchValue(); + return; + } + } + + // Is it an alias? + + if (analyzer.Check('*')) + { + FetchAnchor(true); + return; + } + + // Is it an anchor? + + if (analyzer.Check('&')) + { + FetchAnchor(false); + return; + } + + // Is it a tag? + + if (analyzer.Check('!')) + { + FetchTag(); + return; + } + + // Is it a literal scalar? + + if (analyzer.Check('|') && flowLevel == 0) + { + FetchBlockScalar(true); + return; + } + + // Is it a folded scalar? + + if (analyzer.Check('>') && flowLevel == 0) + { + FetchBlockScalar(false); + return; + } + + // Is it a single-quoted scalar? + + if (analyzer.Check('\'')) + { + FetchFlowScalar(true); + return; + } + + // Is it a double-quoted scalar? + + if (analyzer.Check('"')) + { + FetchFlowScalar(false); + return; + } + + + // Is it a plain scalar? + + // A plain scalar may start with any non-blank characters except + + // '-', '?', ':', ',', '[', ']', '{', '}', + // '#', '&', '*', '!', '|', '>', '\'', '\"', + // '%', '@', '`'. + + // In the block context (and, for the '-' indicator, in the flow context + // too), it may also start with the characters + + // '-', '?', ':' + + // if it is followed by a non-space character. + + // The last rule is more restrictive than the specification requires. + + + var isInvalidPlainScalarCharacter = analyzer.IsWhiteBreakOrZero() || analyzer.Check("-?:,[]{}#&*!|>'\"%@`"); + + var isPlainScalar = + !isInvalidPlainScalarCharacter || + (analyzer.Check('-') && !analyzer.IsWhite(1)) || + (analyzer.Check("?:") && !analyzer.IsWhiteBreakOrZero(1)) || + (simpleKeyAllowed && flowLevel > 0); + + if (isPlainScalar) + { + if (plainScalarFollowedByComment) + { + var startMark = cursor.Mark(); + tokens.Enqueue(new Error("While scanning plain scalar, found a comment between adjacent scalars.", startMark, startMark)); + } + + if (flowScalarFetched || flowCollectionFetched && !startFlowCollectionFetched) + { + if (analyzer.Check(':')) + { + Skip(); + } + } + + flowScalarFetched = false; + flowCollectionFetched = false; + startFlowCollectionFetched = false; + plainScalarFollowedByComment = false; + + FetchPlainScalar(); + return; + } + + if (simpleKeyAllowed && indent >= cursor.LineOffset && analyzer.IsTab()) + { + throw new SyntaxErrorException("While scanning a mapping, found invalid tab as indentation."); + } + + if (analyzer.IsWhiteBreakOrZero()) + { + Skip(); + return; + } + + // If we don't determine the token type so far, it is an error. + var start = cursor.Mark(); + Skip(); + var end = cursor.Mark(); + + throw new SyntaxErrorException(start, end, "While scanning for the next token, found character that cannot start any token."); + } + + private bool CheckWhiteSpace() + { + return analyzer.Check(' ') || ((flowLevel > 0 || !simpleKeyAllowed) && analyzer.Check('\t')); + } + + private void Skip() + { + cursor.Skip(); + analyzer.Buffer.Skip(1); + } + + private void SkipLine() + { + if (analyzer.IsCrLf()) + { + cursor.SkipLineByOffset(2); + analyzer.Buffer.Skip(2); + } + else if (analyzer.IsBreak()) + { + cursor.SkipLineByOffset(1); + analyzer.Buffer.Skip(1); + } + else if (!analyzer.IsZero()) + { + throw new InvalidOperationException("Not at a break."); + } + } + + private void ScanToNextToken() + { + // Until the next token is not find. + + while (true) + { + + // Eat whitespaces. + + // Tabs are allowed: + + // - in the flow context; + // - in the block context, but not at the beginning of the line or + // after '-', '?', or ':' (complex value). + + + while (CheckWhiteSpace()) + { + Skip(); + } + + ProcessComment(); + + // If it is a line break, eat it. + + if (analyzer.IsBreak()) + { + SkipLine(); + + // In the block context, a new line may start a simple key. + + if (flowLevel == 0) + { + simpleKeyAllowed = true; + } + } + else + { + // We have find a token. + + break; + } + } + } + + private void ProcessComment() + { + if (analyzer.Check('#')) + { + var start = cursor.Mark(); + + // Eat '#' + Skip(); + + // Eat leading whitespace + while (analyzer.IsSpace()) + { + Skip(); + } + + using var textBuilder = StringBuilderPool.Rent(); + var text = textBuilder.Builder; + while (!analyzer.IsBreakOrZero()) + { + text.Append(ReadCurrentCharacter()); + } + + if (!SkipComments) + { + var isInline = previous != null + && previous.End.Line == start.Line + && previous.End.Column != 1 + && !(previous is StreamStart); + + tokens.Enqueue(new Comment(text.ToString(), isInline, start, cursor.Mark())); + } + } + } + + private void FetchStreamStart() + { + // Initialize the simple key stack. + + simpleKeys.Push(new SimpleKey()); + + // A simple key is allowed at the beginning of the stream. + + simpleKeyAllowed = true; + + // We have started. + + streamStartProduced = true; + + // Create the STREAM-START token and append it to the queue. + + var mark = cursor.Mark(); + tokens.Enqueue(new StreamStart(mark, mark)); + } + + /// + /// Pop indentation levels from the indents stack until the current level + /// becomes less or equal to the column. For each indentation level, append + /// the BLOCK-END token. + /// + + private void UnrollIndent(int column) + { + // In the flow context, do nothing. + + if (flowLevel != 0) + { + return; + } + + // Loop through the indentation levels in the stack. + + while (indent > column) + { + // Create a token and append it to the queue. + + var mark = cursor.Mark(); + tokens.Enqueue(new BlockEnd(mark, mark)); + + // Pop the indentation level. + + indent = indents.Pop(); + } + } + + /// + /// Produce the STREAM-END token and shut down the scanner. + /// + private void FetchStreamEnd() + { + cursor.ForceSkipLineAfterNonBreak(); + + // Reset the indentation level. + + UnrollIndent(-1); + + // Reset simple keys. + + RemoveSimpleKey(); + + simpleKeyAllowed = false; + + // Create the STREAM-END token and append it to the queue. + + streamEndProduced = true; + var mark = cursor.Mark(); + tokens.Enqueue(new StreamEnd(mark, mark)); + } + + private void FetchDirective() + { + // Reset the indentation level. + + UnrollIndent(-1); + + // Reset simple keys. + + RemoveSimpleKey(); + + simpleKeyAllowed = false; + + // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. + + var token = ScanDirective(); + + // Append the token to the queue. + if (token != null) + { + tokens.Enqueue(token); + } + } + + /// + /// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. + /// + /// Scope: + /// %YAML 1.1 # a comment \n + /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// %TAG !yaml! tag:yaml.org,2002: \n + /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// + private Token? ScanDirective() + { + // Eat '%'. + + var start = cursor.Mark(); + + Skip(); + + // Scan the directive name. + + var name = ScanDirectiveName(start); + + // Is it a YAML directive? + + Token directive; + switch (name) + { + case "YAML": + if (previous is DocumentStart || previous is StreamStart || previous is DocumentEnd) + { + directive = ScanVersionDirectiveValue(start); + } + else + { + throw new SemanticErrorException(start, cursor.Mark(), "While scanning a version directive, did not find preceding ."); + } + break; + + case "TAG": + directive = ScanTagDirectiveValue(start); + break; + + default: + // warning: skipping reserved directive line + while (!analyzer.EndOfInput && !analyzer.Check('#') && !analyzer.IsBreak()) + { + Skip(); + } + return null; + } + + // Eat the rest of the line including any comments. + + while (analyzer.IsWhite()) + { + Skip(); + } + + ProcessComment(); + + // Check if we are at the end of the line. + + if (!analyzer.IsBreakOrZero()) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a directive, did not find expected comment or line break."); + } + + // Eat a line break. + + if (analyzer.IsBreak()) + { + SkipLine(); + } + + return directive; + } + + /// + /// Produce the DOCUMENT-START or DOCUMENT-END token. + /// + + private void FetchDocumentIndicator(bool isStartToken) + { + // Reset the indentation level. + + UnrollIndent(-1); + + // Reset simple keys. + + RemoveSimpleKey(); + + simpleKeyAllowed = false; + + // Consume the token. + + var start = cursor.Mark(); + + Skip(); + Skip(); + Skip(); + + if (isStartToken) + { + tokens.Enqueue(new DocumentStart(start, cursor.Mark())); + } + else + { + Token? errorToken = null; + while (!analyzer.EndOfInput && !analyzer.IsBreak() && !analyzer.Check('#')) + { + if (!analyzer.IsWhite()) + { + errorToken = new Error("While scanning a document end, found invalid content after '...' marker.", start, cursor.Mark()); + break; + } + Skip(); + } + tokens.Enqueue(new DocumentEnd(start, start)); + if (errorToken != null) + { + tokens.Enqueue(errorToken); + } + } + } + + /// + /// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. + /// + + private void FetchFlowCollectionStart(bool isSequenceToken) + { + // The indicators '[' and '{' may start a simple key. + + SaveSimpleKey(); + + // Increase the flow level. + + IncreaseFlowLevel(); + + // A simple key may follow the indicators '[' and '{'. + + simpleKeyAllowed = true; + + // Consume the token. + + var start = cursor.Mark(); + Skip(); + + // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. + + Token token; + if (isSequenceToken) + { + token = new FlowSequenceStart(start, start); + flowSequenceStartLine = token.Start.Line; + } + else + { + token = new FlowMappingStart(start, start); + } + + tokens.Enqueue(token); + startFlowCollectionFetched = true; + } + + /// + /// Increase the flow level and resize the simple key list if needed. + /// + + private void IncreaseFlowLevel() + { + // Reset the simple key on the next level. + + simpleKeys.Push(new SimpleKey()); + + // Increase the flow level. + + ++flowLevel; + } + + /// + /// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. + /// + + private void FetchFlowCollectionEnd(bool isSequenceToken) + { + // Reset any potential simple key on the current flow level. + + RemoveSimpleKey(); + + // Decrease the flow level. + + DecreaseFlowLevel(); + + // No simple keys after the indicators ']' and '}'. + + simpleKeyAllowed = false; + + // Consume the token. + + var start = cursor.Mark(); + Skip(); + + Token? token, errorToken = null; + if (isSequenceToken) + { + if (analyzer.Check('#')) + { + errorToken = new Error("While scanning a flow sequence end, found invalid comment after ']'.", start, start); + } + + token = new FlowSequenceEnd(start, start); + } + else + { + token = new FlowMappingEnd(start, start); + } + + tokens.Enqueue(token); + if (errorToken != null) + { + tokens.Enqueue(errorToken); + } + + flowCollectionFetched = true; + } + + /// + /// Decrease the flow level. + /// + + private void DecreaseFlowLevel() + { + // flowLevel could be zero in case of malformed YAML. + // Since this is handled elsewhere, just ignore it. + if (flowLevel > 0) + { + --flowLevel; + simpleKeys.Pop(); + } + } + + /// + /// Produce the FLOW-ENTRY token. + /// + + private void FetchFlowEntry() + { + // Reset any potential simple keys on the current flow level. + + RemoveSimpleKey(); + + // Simple keys are allowed after ','. + + simpleKeyAllowed = true; + + // Consume the token. + + var start = cursor.Mark(); + Skip(); + + var end = cursor.Mark(); + if (analyzer.Check('#')) + { + tokens.Enqueue(new Error("While scanning a flow entry, found invalid comment after comma.", start, end)); + return; + } + + // Create the FLOW-ENTRY token and append it to the queue. + + tokens.Enqueue(new FlowEntry(start, end)); + } + + /// + /// Produce the BLOCK-ENTRY token. + /// + + private void FetchBlockEntry() + { + // Check if the scanner is in the block context. + + if (flowLevel == 0) + { + // Check if we are allowed to start a new entry. + + if (!simpleKeyAllowed) + { + if (previousAnchor != null) + { + if (previousAnchor.End.Line == cursor.Line) + { + throw new SemanticErrorException(previousAnchor.Start, previousAnchor.End, "Anchor before sequence entry on same line is not allowed."); + } + } + var mark = cursor.Mark(); + tokens.Enqueue(new Error("Block sequence entries are not allowed in this context.", mark, mark)); + } + + // Add the BLOCK-SEQUENCE-START token if needed. + RollIndent(cursor.LineOffset, -1, true, cursor.Mark()); + } + else + { + + // It is an error for the '-' indicator to occur in the flow context, + // but we let the Parser detect and report about it because the Parser + // is able to point to the context. + + } + + // Reset any potential simple keys on the current flow level. + + RemoveSimpleKey(); + + // Simple keys are allowed after '-'. + + simpleKeyAllowed = true; + + // Consume the token. + + var start = cursor.Mark(); + Skip(); + + // Create the BLOCK-ENTRY token and append it to the queue. + + tokens.Enqueue(new BlockEntry(start, cursor.Mark())); + } + + /// + /// Produce the KEY token. + /// + + private void FetchKey() + { + // In the block context, additional checks are required. + + if (flowLevel == 0) + { + // Check if we are allowed to start a new key (not necessary simple). + + if (!simpleKeyAllowed) + { + var mark = cursor.Mark(); + throw new SyntaxErrorException(mark, mark, "Mapping keys are not allowed in this context."); + } + + // Add the BLOCK-MAPPING-START token if needed. + + RollIndent(cursor.LineOffset, -1, false, cursor.Mark()); + } + + // Reset any potential simple keys on the current flow level. + + RemoveSimpleKey(); + + // Simple keys are allowed after '?' in the block context. + + simpleKeyAllowed = flowLevel == 0; + + // Consume the token. + + var start = cursor.Mark(); + Skip(); + + // Create the KEY token and append it to the queue. + + tokens.Enqueue(new Key(start, cursor.Mark())); + } + + /// + /// Produce the VALUE token. + /// + + private void FetchValue() + { + var simpleKey = simpleKeys.Peek(); + + // Have we find a simple key? + + if (simpleKey.IsPossible) + { + // Create the KEY token and insert it into the queue. + + tokens.Insert(simpleKey.TokenNumber - tokensParsed, new Key(simpleKey.Mark, simpleKey.Mark)); + + // In the block context, we may need to add the BLOCK-MAPPING-START token. + + RollIndent(simpleKey.LineOffset, simpleKey.TokenNumber, false, simpleKey.Mark); + + // Remove the simple key. + + simpleKey.MarkAsImpossible(); + + // A simple key cannot follow another simple key. + + simpleKeyAllowed = false; + } + else + { + // The ':' indicator follows a complex key. + + // Simple keys after ':' are allowed in the block context. + + var localSimpleKeyAllowed = flowLevel == 0; + + // In the block context, extra checks are required. + + if (localSimpleKeyAllowed) + { + // Check if we are allowed to start a complex value. + + if (!simpleKeyAllowed) + { + var mark = cursor.Mark(); + tokens.Enqueue(new Error("Mapping values are not allowed in this context.", mark, mark)); + return; + } + + // Add the BLOCK-MAPPING-START token if needed. + + RollIndent(cursor.LineOffset, -1, false, cursor.Mark()); + + // Check if we are dealing with empty key. + + if (cursor.LineOffset == 0 && simpleKey.LineOffset == 0) + { + // Create the KEY token and insert it into the queue. + + tokens.Insert(tokens.Count, new Key(simpleKey.Mark, simpleKey.Mark)); + + // A simple key cannot follow another simple key. + + localSimpleKeyAllowed = false; + } + } + + simpleKeyAllowed = localSimpleKeyAllowed; + } + + // Consume the token. + + var start = cursor.Mark(); + Skip(); + + // Create the VALUE token and append it to the queue. + + tokens.Enqueue(new Value(start, cursor.Mark())); + } + + /// + /// Push the current indentation level to the stack and set the new level + /// the current column is greater than the indentation level. In this case, + /// append or insert the specified token into the token queue. + /// + private void RollIndent(int column, int number, bool isSequence, Mark position) + { + // In the flow context, do nothing. + + if (flowLevel > 0) + { + return; + } + + if (indent < column) + { + + // Push the current indentation level to the stack and set the new + // indentation level. + + + indents.Push(indent); + + indent = column; + + // Create a token and insert it into the queue. + + Token token; + if (isSequence) + { + token = new BlockSequenceStart(position, position); + } + else + { + token = new BlockMappingStart(position, position); + } + + if (number == -1) + { + tokens.Enqueue(token); + } + else + { + tokens.Insert(number - tokensParsed, token); + } + } + } + + /// + /// Produce the ALIAS or ANCHOR token. + /// + + private void FetchAnchor(bool isAlias) + { + // An anchor or an alias could be a simple key. + + SaveSimpleKey(); + + // A simple key cannot follow an anchor or an alias. + + simpleKeyAllowed = false; + + // Create the ALIAS or ANCHOR token and append it to the queue. + + tokens.Enqueue(ScanAnchor(isAlias)); + } + + private Token ScanAnchor(bool isAlias) + { + // Eat the indicator character. + + var start = cursor.Mark(); + + Skip(); + + var isAliasKey = false; + if (isAlias) + { + var key = simpleKeys.Peek(); + isAliasKey = key.IsRequired && key.IsPossible; + } + + // Consume the value. + // YAML 1.2 - section 6.9.2."Node Anchors" specifies disallowed characters + // in the anchor name as follows: + // '[', ']', '{', '}' and ',' + // ref: https://yaml.org/spec/1.2/spec.html#id2785586 + + using var valueBuilder = StringBuilderPool.Rent(); + var value = valueBuilder.Builder; + while (!analyzer.IsWhiteBreakOrZero()) + { + // Anchor: read all allowed characters + + // Alias: read all allowed characters except colon (':'); read colon when token is: + // * not used in key OR + // * used in key and colon is not last character + + if (!analyzer.Check("[]{},") && + !(isAliasKey && analyzer.Check(':') && analyzer.IsWhiteBreakOrZero(1))) + { + value.Append(ReadCurrentCharacter()); + } + else + { + break; + } + } + + // Check if length of the anchor is greater than 0 and it is followed by + // a whitespace character or one of the indicators: + + // '?', ':', ',', ']', '}', '%', '@', '`'. + + + if (value.Length == 0 || !(analyzer.IsWhiteBreakOrZero() || analyzer.Check("?:,]}%@`"))) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning an anchor or alias, found value containing disallowed: []{},"); + } + + // Create a token. + var name = new AnchorName(value.ToString()); + if (isAlias) + { + return new AnchorAlias(name, start, cursor.Mark()); + } + else + { + return previousAnchor = new Anchor(name, start, cursor.Mark()); + } + } + + /// + /// Produce the TAG token. + /// + + private void FetchTag() + { + // A tag could be a simple key. + + SaveSimpleKey(); + + // A simple key cannot follow a tag. + + simpleKeyAllowed = false; + + // Create the TAG token and append it to the queue. + + tokens.Enqueue(ScanTag()); + } + + /// + /// Scan a TAG token. + /// + + Token ScanTag() + { + var start = cursor.Mark(); + + // Check if the tag is in the canonical form. + + string handle; + string suffix; + + if (analyzer.Check('<', 1)) + { + // Set the handle to '' + + handle = string.Empty; + + // Eat '!<' + + Skip(); + Skip(); + + // Consume the tag value. + + suffix = ScanTagUri(null, start); + + // Check for '>' and eat it. + + if (!analyzer.Check('>')) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, did not find the expected '>'."); + } + + Skip(); + } + else + { + // The tag has either the '!suffix' or the '!handle!suffix' form. + + // First, try to scan a handle. + + var firstPart = ScanTagHandle(false, start); + + // Check if it is, indeed, handle. + + if (firstPart.Length > 1 && firstPart[0] == '!' && firstPart[firstPart.Length - 1] == '!') + { + handle = firstPart; + + // Scan the suffix now. + + suffix = ScanTagUri(null, start); + } + else + { + // It wasn't a handle after all. Scan the rest of the tag. + + suffix = ScanTagUri(firstPart, start); + + // Set the handle to '!'. + + handle = "!"; + + + // A special case: the '!' tag. Set the handle to '' and the + // suffix to '!'. + + + if (suffix.Length == 0) + { + suffix = handle; + handle = string.Empty; + } + } + } + + // Check the character which ends the tag. + + if (!analyzer.IsWhiteBreakOrZero() && !analyzer.Check(',')) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, did not find expected whitespace, comma or line break."); + } + + // Create a token. + + return new Tag(handle, suffix, start, cursor.Mark()); + } + + /// + /// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. + /// + + private void FetchBlockScalar(bool isLiteral) + { + // A block scalar can be a simple key + + SaveSimpleKey(); + + // A simple key may follow a block scalar. + + simpleKeyAllowed = true; + + // Create the SCALAR token and append it to the queue. + + tokens.Enqueue(ScanBlockScalar(isLiteral)); + } + + /// + /// Scan a block scalar. + /// + + Token ScanBlockScalar(bool isLiteral) + { + using var valueBuilder = StringBuilderPool.Rent(); + var value = valueBuilder.Builder; + + using var leadingBreakBuilder = StringBuilderPool.Rent(); + var leadingBreak = leadingBreakBuilder.Builder; + + using var trailingBreaksBuilder = StringBuilderPool.Rent(); + var trailingBreaks = trailingBreaksBuilder.Builder; + + var chomping = 0; + var increment = 0; + var currentIndent = 0; + var leadingBlank = false; + bool? isFirstLine = null; + + // Eat the indicator '|' or '>'. + + var start = cursor.Mark(); + + Skip(); + + // Check for a chomping indicator. + + if (analyzer.Check("+-")) + { + // Set the chomping method and eat the indicator. + + chomping = analyzer.Check('+') ? +1 : -1; + + Skip(); + + // Check for an indentation indicator. + + if (analyzer.IsDigit()) + { + // Check that the indentation is greater than 0. + + if (analyzer.Check('0')) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, found an indentation indicator equal to 0."); + } + + // Get the indentation level and eat the indicator. + + increment = analyzer.AsDigit(); + + Skip(); + } + } + + // Do the same as above, but in the opposite order. + + else if (analyzer.IsDigit()) + { + if (analyzer.Check('0')) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, found an indentation indicator equal to 0."); + } + + increment = analyzer.AsDigit(); + + Skip(); + + if (analyzer.Check("+-")) + { + chomping = analyzer.Check('+') ? +1 : -1; + + Skip(); + } + } + + // Check if there is a comment without whitespace after block scalar indicator (yaml-test-suite: X4QW). + + if (analyzer.Check('#')) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, found a comment without whtespace after '>' indicator."); + } + + // Eat whitespaces and comments to the end of the line. + + while (analyzer.IsWhite()) + { + Skip(); + } + + ProcessComment(); + + // Check if we are at the end of the line. + + if (!analyzer.IsBreakOrZero()) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a block scalar, did not find expected comment or line break."); + } + + // Eat a line break. + + if (analyzer.IsBreak()) + { + SkipLine(); + if (!isFirstLine.HasValue) + { + isFirstLine = true; + } + else if (isFirstLine == true) + { + isFirstLine = false; + } + } + + var end = cursor.Mark(); + + // Set the indentation level if it was specified. + + if (increment != 0) + { + currentIndent = indent >= 0 ? indent + increment : increment; + } + + // Scan the leading line breaks and determine the indentation level if needed. + + currentIndent = ScanBlockScalarBreaks(currentIndent, trailingBreaks, isLiteral, ref end, ref isFirstLine); + isFirstLine = false; + + // Scan the block scalar content. + + while (cursor.LineOffset == currentIndent && !analyzer.IsZero() && !IsDocumentEnd()) + { + // We are at the beginning of a non-empty line. + + // Is it a trailing whitespace? + + var trailingBlank = analyzer.IsWhite(); + + // Check if we need to fold the leading line break. + + if (!isLiteral && StartsWith(leadingBreak, '\n') && !leadingBlank && !trailingBlank) + { + // Do we need to join the lines by space? + + if (trailingBreaks.Length == 0) + { + value.Append(' '); + } + + leadingBreak.Length = 0; + } + else + { + value.Append(leadingBreak.ToString()); + leadingBreak.Length = 0; + } + + // Append the remaining line breaks. + + value.Append(trailingBreaks.ToString()); + trailingBreaks.Length = 0; + + // Is it a leading whitespace? + + leadingBlank = analyzer.IsWhite(); + + // Consume the current line. + + while (!analyzer.IsBreakOrZero()) + { + value.Append(ReadCurrentCharacter()); + } + + // Consume the line break. + var lineBreak = ReadLine(); + if (lineBreak != '\0') + { + leadingBreak.Append(lineBreak); + } + + // Eat the following indentation spaces and line breaks. + + currentIndent = ScanBlockScalarBreaks(currentIndent, trailingBreaks, isLiteral, ref end, ref isFirstLine); + } + + // Chomp the tail. + + if (chomping != -1) + { + value.Append(leadingBreak); + } + if (chomping == 1) + { + value.Append(trailingBreaks); + } + + // Create a token. + + var style = isLiteral ? ScalarStyle.Literal : ScalarStyle.Folded; + return new Scalar(value.ToString(), style, start, end); + } + + /// + /// Scan indentation spaces and line breaks for a block scalar. Determine the + /// indentation level if needed. + /// + + private int ScanBlockScalarBreaks(int currentIndent, StringBuilder breaks, bool isLiteral, ref Mark end, ref bool? isFirstLine) + { + var maxIndent = 0; + var indentOfFirstLine = -1; + + end = cursor.Mark(); + + // Eat the indentation spaces and line breaks. + + while (true) + { + // Eat the indentation spaces. + + while ((currentIndent == 0 || cursor.LineOffset < currentIndent) && analyzer.IsSpace()) + { + Skip(); + } + + if (cursor.LineOffset > maxIndent) + { + maxIndent = cursor.LineOffset; + } + + // Have we find a non-empty line? + + if (!analyzer.IsBreak()) + { + if (isLiteral && isFirstLine == true) + { + var localIndent = cursor.LineOffset; + var i = 0; + while (!analyzer.IsBreak(i) && analyzer.IsSpace(i)) + { + ++i; + ++localIndent; + } + + if (analyzer.IsBreak(i) && localIndent > cursor.LineOffset) + { + isFirstLine = false; + indentOfFirstLine = localIndent; + } + } + break; + } + + if (isFirstLine == true) + { + isFirstLine = false; + indentOfFirstLine = cursor.LineOffset; + } + + // Consume the line break. + + breaks.Append(ReadLine()); + + end = cursor.Mark(); + } + + // Check if first line after literal is all spaces and count of spaces is more than "1 + currentIndent". + + if (isLiteral && indentOfFirstLine > 1 && currentIndent < indentOfFirstLine - 1) + { + // W9L4 + throw new SemanticErrorException(end, cursor.Mark(), "While scanning a literal block scalar, found extra spaces in first line."); + } + + if (!isLiteral && maxIndent > cursor.LineOffset && indentOfFirstLine > -1) + { + // S98Z + throw new SemanticErrorException(end, cursor.Mark(), "While scanning a literal block scalar, found more spaces in lines above first content line."); + } + + // Determine the indentation level if needed. + + if (currentIndent == 0 && (cursor.LineOffset > 0 || indent > -1)) + { + currentIndent = Math.Max(maxIndent, Math.Max(indent + 1, 1)); + } + + return currentIndent; + } + + /// + /// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. + /// + + private void FetchFlowScalar(bool isSingleQuoted) + { + // A plain scalar could be a simple key. + + SaveSimpleKey(); + + // A simple key cannot follow a flow scalar. + + simpleKeyAllowed = false; + + // Indicates the adjacent flow scalar that a prior flow scalar has been fetched. + + flowScalarFetched = true; + + // Create the SCALAR token and append it to the queue. + + tokens.Enqueue(ScanFlowScalar(isSingleQuoted)); + + // Check if there is a comment subsequently after double-quoted scalar without space. + + if (!isSingleQuoted && analyzer.Check('#')) + { + var start = cursor.Mark(); + tokens.Enqueue(new Error("While scanning a flow sequence end, found invalid comment after double-quoted scalar.", start, start)); + } + } + + /// + /// Scan a quoted scalar. + /// + + private Token ScanFlowScalar(bool isSingleQuoted) + { + // Eat the left quote. + + var start = cursor.Mark(); + + Skip(); + + // Consume the content of the quoted scalar. + + using var valueBuilder = StringBuilderPool.Rent(); + var value = valueBuilder.Builder; + + using var whitespacesBuilder = StringBuilderPool.Rent(); + var whitespaces = whitespacesBuilder.Builder; + + using var leadingBreakBuilder = StringBuilderPool.Rent(); + var leadingBreak = leadingBreakBuilder.Builder; + + using var trailingBreaksBuilder = StringBuilderPool.Rent(); + var trailingBreaks = trailingBreaksBuilder.Builder; + + var hasLeadingBlanks = false; + + while (true) + { + // Check that there are no document indicators at the beginning of the line. + + if (IsDocumentIndicator()) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, found unexpected document indicator."); + } + + // Check for EOF. + + if (analyzer.IsZero()) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, found unexpected end of stream."); + } + + if (hasLeadingBlanks && !isSingleQuoted && indent >= cursor.LineOffset) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a multi-line double-quoted scalar, found wrong indentation."); + } + + hasLeadingBlanks = false; + + // Consume non-blank characters. + + while (!analyzer.IsWhiteBreakOrZero()) + { + // Check for an escaped single quote. + + if (isSingleQuoted && analyzer.Check('\'', 0) && analyzer.Check('\'', 1)) + { + value.Append('\''); + Skip(); + Skip(); + } + + // Check for the right quote. + + else if (analyzer.Check(isSingleQuoted ? '\'' : '"')) + { + break; + } + + // Check for an escaped line break. + + else if (!isSingleQuoted && analyzer.Check('\\') && analyzer.IsBreak(1)) + { + Skip(); + SkipLine(); + hasLeadingBlanks = true; + break; + } + + // Check for an escape sequence. + + else if (!isSingleQuoted && analyzer.Check('\\')) + { + var codeLength = 0; + + // Check the escape character. + + var escapeCharacter = analyzer.Peek(1); + switch (escapeCharacter) + { + case 'x': + codeLength = 2; + break; + + case 'u': + codeLength = 4; + break; + + case 'U': + codeLength = 8; + break; + + default: + char unescapedCharacter; + if (SimpleEscapeCodes.TryGetValue(escapeCharacter, out unescapedCharacter)) + { + value.Append(unescapedCharacter); + } + else + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, found unknown escape character."); + } + break; + } + + Skip(); + Skip(); + + // Consume an arbitrary escape code. + + if (codeLength > 0) + { + var character = 0; + + // Scan the character value. + + for (var k = 0; k < codeLength; ++k) + { + if (!analyzer.IsHex(k)) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, did not find expected hexadecimal number."); + } + character = ((character << 4) + analyzer.AsHex(k)); + } + + // Check the value and write the character. + + if ((character >= 0xD800 && character <= 0xDFFF) || character > 0x10FFFF) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a quoted scalar, found invalid Unicode character escape code."); + } + + value.Append(char.ConvertFromUtf32(character)); + + // Advance the pointer. + + for (var k = 0; k < codeLength; ++k) + { + Skip(); + } + } + } + else + { + // It is a non-escaped non-blank character. + + value.Append(ReadCurrentCharacter()); + } + } + + // Check if we are at the end of the scalar. + + if (analyzer.Check(isSingleQuoted ? '\'' : '"')) + { + break; + } + + // Consume blank characters. + + while (analyzer.IsWhite() || analyzer.IsBreak()) + { + if (analyzer.IsWhite()) + { + // Consume a space or a tab character. + + if (!hasLeadingBlanks) + { + whitespaces.Append(ReadCurrentCharacter()); + } + else + { + Skip(); + } + } + else + { + // Check if it is a first line break. + + if (!hasLeadingBlanks) + { + whitespaces.Length = 0; + leadingBreak.Append(ReadLine()); + hasLeadingBlanks = true; + } + else + { + trailingBreaks.Append(ReadLine()); + } + } + } + + // Join the whitespaces or fold line breaks. + + if (hasLeadingBlanks) + { + // Do we need to fold line breaks? + + if (StartsWith(leadingBreak, '\n')) + { + if (trailingBreaks.Length == 0) + { + value.Append(' '); + } + else + { + value.Append(trailingBreaks.ToString()); + } + } + else + { + value.Append(leadingBreak.ToString()); + value.Append(trailingBreaks.ToString()); + } + leadingBreak.Length = 0; + trailingBreaks.Length = 0; + } + else + { + value.Append(whitespaces.ToString()); + whitespaces.Length = 0; + } + } + + // Eat the right quote. + + Skip(); + + return new Scalar(value.ToString(), isSingleQuoted ? ScalarStyle.SingleQuoted : ScalarStyle.DoubleQuoted, start, cursor.Mark()); + } + + /// + /// Produce the SCALAR(...,plain) token. + /// + + private void FetchPlainScalar() + { + // A plain scalar could be a simple key. + + SaveSimpleKey(); + + // A simple key cannot follow a flow scalar. + + simpleKeyAllowed = false; + + // Create the SCALAR token and append it to the queue. + var isMultiline = false; + var scalar = ScanPlainScalar(ref isMultiline); + lastScalar = scalar; + if (isMultiline && analyzer.Check(':') && flowLevel == 0 && indent < cursor.LineOffset) + { + tokens.Enqueue(new Error("While scanning a multiline plain scalar, found invalid mapping.", cursor.Mark(), cursor.Mark())); + } + tokens.Enqueue(scalar); + } + + /// + /// Scan a plain scalar. + /// + + private Scalar ScanPlainScalar(ref bool isMultiline) + { + using var valueBuilder = StringBuilderPool.Rent(); + var value = valueBuilder.Builder; + + using var whitespacesBuilder = StringBuilderPool.Rent(); + var whitespaces = whitespacesBuilder.Builder; + + using var leadingBreakBuilder = StringBuilderPool.Rent(); + var leadingBreak = leadingBreakBuilder.Builder; + + using var trailingBreaksBuilder = StringBuilderPool.Rent(); + var trailingBreaks = trailingBreaksBuilder.Builder; + + var hasLeadingBlanks = false; + var currentIndent = indent + 1; + + var start = cursor.Mark(); + var end = start; + + var key = simpleKeys.Peek(); + + // Consume the content of the plain scalar. + + while (true) + { + // Check for a document indicator. + + if (IsDocumentIndicator()) + { + break; + } + + // Check for a comment. + + if (analyzer.Check('#')) + { + if (indent < 0 && flowLevel == 0) + { + plainScalarFollowedByComment = true; + } + break; + } + + var isAliasValue = analyzer.Check('*') && !(key.IsPossible && key.IsRequired); + + // Consume non-blank characters. + while (!analyzer.IsWhiteBreakOrZero()) + { + // Check for indicators that may end a plain scalar. + + if (analyzer.Check(':') && + !isAliasValue && + (analyzer.IsWhiteBreakOrZero(1) || + (flowLevel > 0 && analyzer.Check(',', 1))) || + (flowLevel > 0 && analyzer.Check(",[]{}"))) + { + if (flowLevel == 0 && !key.IsPossible) + { + tokens.Enqueue(new Error("While scanning a plain scalar value, found invalid mapping.", cursor.Mark(), cursor.Mark())); + } + break; + } + + // Check if we need to join whitespaces and breaks. + + if (hasLeadingBlanks || whitespaces.Length > 0) + { + if (hasLeadingBlanks) + { + // Do we need to fold line breaks? + + if (StartsWith(leadingBreak, '\n')) + { + if (trailingBreaks.Length == 0) + { + value.Append(' '); + } + else + { + value.Append(trailingBreaks); + } + } + else + { + value.Append(leadingBreak); + value.Append(trailingBreaks); + } + + leadingBreak.Length = 0; + trailingBreaks.Length = 0; + + hasLeadingBlanks = false; + } + else + { + value.Append(whitespaces); + whitespaces.Length = 0; + } + } + if (flowLevel > 0 && cursor.LineOffset < currentIndent) + { + throw new Exception(); + } + // Copy the character. + + value.Append(ReadCurrentCharacter()); + + end = cursor.Mark(); + } + + // Is it the end? + + if (!(analyzer.IsWhite() || analyzer.IsBreak())) + { + break; + } + + // Consume blank characters. + + while (analyzer.IsWhite() || analyzer.IsBreak()) + { + if (analyzer.IsWhite()) + { + // Check for tab character that abuse indentation. + + if (hasLeadingBlanks && cursor.LineOffset < currentIndent && analyzer.IsTab()) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a plain scalar, found a tab character that violate indentation."); + } + + // Consume a space or a tab character. + + if (!hasLeadingBlanks) + { + whitespaces.Append(ReadCurrentCharacter()); + } + else + { + Skip(); + } + } + else + { + isMultiline = true; + + // Check if it is a first line break. + + if (!hasLeadingBlanks) + { + whitespaces.Length = 0; + leadingBreak.Append(ReadLine()); + hasLeadingBlanks = true; + } + else + { + trailingBreaks.Append(ReadLine()); + } + } + } + + // Check indentation level. + + if (flowLevel == 0 && cursor.LineOffset < currentIndent) + { + break; + } + } + + // Note that we change the 'simple_key_allowed' flag. + + if (hasLeadingBlanks) + { + simpleKeyAllowed = true; + } + + // Create a token. + + return new Scalar(value.ToString(), ScalarStyle.Plain, start, end); + } + + + /// + /// Remove a potential simple key at the current flow level. + /// + + private void RemoveSimpleKey() + { + var key = simpleKeys.Peek(); + + if (key.IsPossible && key.IsRequired) + { + // If the key is required, it is an error. + + throw new SyntaxErrorException(key.Mark, key.Mark, "While scanning a simple key, could not find expected ':'."); + } + + // Remove the key from the stack. + + key.MarkAsImpossible(); + } + + /// + /// Scan the directive name. + /// + /// Scope: + /// %YAML 1.1 # a comment \n + /// ^^^^ + /// %TAG !yaml! tag:yaml.org,2002: \n + /// ^^^ + /// + private string ScanDirectiveName(in Mark start) + { + using var nameBuilder = StringBuilderPool.Rent(); + var name = nameBuilder.Builder; + + // Consume the directive name. + + while (analyzer.IsAlphaNumericDashOrUnderscore()) + { + name.Append(ReadCurrentCharacter()); + } + + // Check if the name is empty. + + if (name.Length == 0) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a directive, could not find expected directive name."); + } + + // Check for end of stream + + if (analyzer.EndOfInput) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a directive, found unexpected end of stream."); + } + + // Check for an blank character after the name. + + if (!analyzer.IsWhiteBreakOrZero()) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a directive, found unexpected non-alphabetical character."); + } + + return name.ToString(); + } + + private void SkipWhitespaces() + { + // Eat whitespaces. + + while (analyzer.IsWhite()) + { + Skip(); + } + } + + /// + /// Scan the value of VERSION-DIRECTIVE. + /// + /// Scope: + /// %YAML 1.1 # a comment \n + /// ^^^^^^ + /// + private Token ScanVersionDirectiveValue(in Mark start) + { + SkipWhitespaces(); + + // Consume the major version number. + + var major = ScanVersionDirectiveNumber(start); + + // Eat '.'. + + if (!analyzer.Check('.')) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %YAML directive, did not find expected digit or '.' character."); + } + + Skip(); + + // Consume the minor version number. + + var minor = ScanVersionDirectiveNumber(start); + + return new VersionDirective(new Version(major, minor), start, start); + } + + /// + /// Scan the value of a TAG-DIRECTIVE token. + /// + /// Scope: + /// %TAG !yaml! tag:yaml.org,2002: \n + /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// + private Token ScanTagDirectiveValue(in Mark start) + { + SkipWhitespaces(); + + // Scan a handle. + + var handle = ScanTagHandle(true, start); + + // Expect a whitespace. + + if (!analyzer.IsWhite()) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %TAG directive, did not find expected whitespace."); + } + + SkipWhitespaces(); + + // Scan a prefix. + + var prefix = ScanTagUri(null, start); + + // Expect a whitespace or line break. + + if (!analyzer.IsWhiteBreakOrZero()) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %TAG directive, did not find expected whitespace or line break."); + } + + return new TagDirective(handle, prefix, start, start); + } + + /// + /// Scan a tag. + /// + + private string ScanTagUri(string? head, Mark start) + { + using var tagBuilder = StringBuilderPool.Rent(); + var tag = tagBuilder.Builder; + + if (head != null && head.Length > 1) + { + tag.Append(head.Substring(1)); + } + + // Scan the tag. + + // The set of characters that may appear in URI is as follows: + + // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', + // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', + // '%'. + + + while (analyzer.IsAlphaNumericDashOrUnderscore() || analyzer.Check(";/?:@&=+$.!~*'()[]%") || + (analyzer.Check(',') && !analyzer.IsBreak(1))) + { + // Check if it is a URI-escape sequence. + + if (analyzer.Check('%')) + { + tag.Append(ScanUriEscapes(start)); + } + else if (analyzer.Check('+')) + { + tag.Append(' '); + Skip(); + } + else + { + tag.Append(ReadCurrentCharacter()); + } + } + + // Check if the tag is non-empty. + + if (tag.Length == 0) + { + return string.Empty; + } + + var result = tag.ToString(); + if (result.EndsWith(",")) + { + throw new SyntaxErrorException(cursor.Mark(), cursor.Mark(), "Unexpected comma at end of tag"); + } + + return result; + } + + private static readonly byte[] EmptyBytes = new byte[0]; + + /// + /// Decode an URI-escape sequence corresponding to a single UTF-8 character. + /// + + private string ScanUriEscapes(in Mark start) + { + // Decode the required number of characters. + + var charBytes = EmptyBytes; + var nextInsertionIndex = 0; + var width = 0; + do + { + // Check for a URI-escaped octet. + + if (!(analyzer.Check('%') && analyzer.IsHex(1) && analyzer.IsHex(2))) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, did not find URI escaped octet."); + } + + // Get the octet. + + var octet = (analyzer.AsHex(1) << 4) + analyzer.AsHex(2); + + // If it is the leading octet, determine the length of the UTF-8 sequence. + + if (width == 0) + { + width = (octet & 0x80) == 0x00 ? 1 : + (octet & 0xE0) == 0xC0 ? 2 : + (octet & 0xF0) == 0xE0 ? 3 : + (octet & 0xF8) == 0xF0 ? 4 : 0; + + if (width == 0) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, found an incorrect leading UTF-8 octet."); + } + + charBytes = new byte[width]; + } + else + { + // Check if the trailing octet is correct. + + if ((octet & 0xC0) != 0x80) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, found an incorrect trailing UTF-8 octet."); + } + } + + // Copy the octet and move the pointers. + + charBytes[nextInsertionIndex++] = (byte)octet; + + Skip(); + Skip(); + Skip(); + } + while (--width > 0); + + var result = Encoding.UTF8.GetString(charBytes, 0, nextInsertionIndex); + + if (result.Length == 0 || result.Length > 2) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, found an incorrect UTF-8 sequence."); + } + + return result; + } + + /// + /// Scan a tag handle. + /// + + private string ScanTagHandle(bool isDirective, Mark start) + { + + // Check the initial '!' character. + + if (!analyzer.Check('!')) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag, did not find expected '!'."); + } + + // Copy the '!' character. + + using var tagHandleBuilder = StringBuilderPool.Rent(); + var tagHandle = tagHandleBuilder.Builder; + tagHandle.Append(ReadCurrentCharacter()); + + // Copy all subsequent alphabetical and numerical characters. + + while (analyzer.IsAlphaNumericDashOrUnderscore()) + { + tagHandle.Append(ReadCurrentCharacter()); + } + + // Check if the trailing character is '!' and copy it. + + if (analyzer.Check('!')) + { + tagHandle.Append(ReadCurrentCharacter()); + } + else + { + + // It's either the '!' tag or not really a tag handle. If it's a %TAG + // directive, it's an error. If it's a tag token, it must be a part of + // URI. + + + if (isDirective && (tagHandle.Length != 1 || tagHandle[0] != '!')) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a tag directive, did not find expected '!'."); + } + } + + return tagHandle.ToString(); + } + + /// + /// Scan the version number of VERSION-DIRECTIVE. + /// + /// Scope: + /// %YAML 1.1 # a comment \n + /// ^ + /// %YAML 1.1 # a comment \n + /// ^ + /// + private int ScanVersionDirectiveNumber(in Mark start) + { + var value = 0; + var length = 0; + + // Repeat while the next character is digit. + + while (analyzer.IsDigit()) + { + // Check if the number is too long. + + if (++length > MaxVersionNumberLength) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %YAML directive, found extremely long version number."); + } + + value = value * 10 + analyzer.AsDigit(); + + Skip(); + } + + // Check if the number was present. + + if (length == 0) + { + throw new SyntaxErrorException(start, cursor.Mark(), "While scanning a %YAML directive, did not find expected version number."); + } + + return value; + } + + /// + /// Check if a simple key may start at the current position and add it if + /// needed. + /// + + private void SaveSimpleKey() + { + + // A simple key is required at the current position if the scanner is in + // the block context and the current column coincides with the indentation + // level. + + + var isRequired = (flowLevel == 0 && indent == cursor.LineOffset); + + + // A simple key is required only when it is the first token in the current + // line. Therefore it is always allowed. But we add a check anyway. + + + Debug.Assert(simpleKeyAllowed || !isRequired, "Can't require a simple key and disallow it at the same time."); // Impossible. + + + // If the current position may start a simple key, save it. + + + if (simpleKeyAllowed) + { + var key = new SimpleKey(isRequired, tokensParsed + tokens.Count, cursor); + + RemoveSimpleKey(); + + simpleKeys.Pop(); + simpleKeys.Push(key); + } + } + } +} diff --git a/YamlDotNet/Core/SemanticErrorException.cs b/YamlDotNet/Core/SemanticErrorException.cs index 5209cc867..eb504c862 100644 --- a/YamlDotNet/Core/SemanticErrorException.cs +++ b/YamlDotNet/Core/SemanticErrorException.cs @@ -1,58 +1,58 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - /// - /// Exception that is thrown when a semantic error is detected on a YAML stream. - /// - public class SemanticErrorException : YamlException - { - /// - /// Initializes a new instance of the class. - /// - /// The message. - public SemanticErrorException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class. - /// - public SemanticErrorException(in Mark start, in Mark end, string message) - : base(start, end, message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner. - public SemanticErrorException(string message, Exception inner) - : base(message, inner) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + /// + /// Exception that is thrown when a semantic error is detected on a YAML stream. + /// + public class SemanticErrorException : YamlException + { + /// + /// Initializes a new instance of the class. + /// + /// The message. + public SemanticErrorException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + public SemanticErrorException(in Mark start, in Mark end, string message) + : base(start, end, message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner. + public SemanticErrorException(string message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/YamlDotNet/Core/SimpleKey.cs b/YamlDotNet/Core/SimpleKey.cs index 6241257fd..f7f31ad6e 100644 --- a/YamlDotNet/Core/SimpleKey.cs +++ b/YamlDotNet/Core/SimpleKey.cs @@ -1,56 +1,56 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core -{ - internal sealed class SimpleKey - { - private readonly Cursor cursor; - - public bool IsPossible { get; private set; } - - public void MarkAsImpossible() - { - IsPossible = false; - } - - public bool IsRequired { get; } - public int TokenNumber { get; } - public int Index => cursor.Index; - public int Line => cursor.Line; - public int LineOffset => cursor.LineOffset; - - public Mark Mark => cursor.Mark(); - - public SimpleKey() - { - cursor = new Cursor(); - } - - public SimpleKey(bool isRequired, int tokenNumber, Cursor cursor) - { - IsPossible = true; - IsRequired = isRequired; - TokenNumber = tokenNumber; - this.cursor = new Cursor(cursor); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core +{ + internal sealed class SimpleKey + { + private readonly Cursor cursor; + + public bool IsPossible { get; private set; } + + public void MarkAsImpossible() + { + IsPossible = false; + } + + public bool IsRequired { get; } + public int TokenNumber { get; } + public int Index => cursor.Index; + public int Line => cursor.Line; + public int LineOffset => cursor.LineOffset; + + public Mark Mark => cursor.Mark(); + + public SimpleKey() + { + cursor = new Cursor(); + } + + public SimpleKey(bool isRequired, int tokenNumber, Cursor cursor) + { + IsPossible = true; + IsRequired = isRequired; + TokenNumber = tokenNumber; + this.cursor = new Cursor(cursor); + } + } +} diff --git a/YamlDotNet/Core/StringLookAheadBuffer.cs b/YamlDotNet/Core/StringLookAheadBuffer.cs index 8114116af..38640c7be 100644 --- a/YamlDotNet/Core/StringLookAheadBuffer.cs +++ b/YamlDotNet/Core/StringLookAheadBuffer.cs @@ -1,61 +1,61 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - internal sealed class StringLookAheadBuffer : ILookAheadBuffer - { - private readonly string value; - - public int Position { get; private set; } - - public StringLookAheadBuffer(string value) - { - this.value = value; - } - - public int Length => value.Length; - - public bool EndOfInput => IsOutside(Position); - - public char Peek(int offset) - { - var index = Position + offset; - return IsOutside(index) ? '\0' : value[index]; - } - - private bool IsOutside(int index) - { - return index >= value.Length; - } - - public void Skip(int length) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length), "The length must be positive."); - } - Position += length; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + internal sealed class StringLookAheadBuffer : ILookAheadBuffer + { + private readonly string value; + + public int Position { get; private set; } + + public StringLookAheadBuffer(string value) + { + this.value = value; + } + + public int Length => value.Length; + + public bool EndOfInput => IsOutside(Position); + + public char Peek(int offset) + { + var index = Position + offset; + return IsOutside(index) ? '\0' : value[index]; + } + + private bool IsOutside(int index) + { + return index >= value.Length; + } + + public void Skip(int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), "The length must be positive."); + } + Position += length; + } + } +} diff --git a/YamlDotNet/Core/SyntaxErrorException.cs b/YamlDotNet/Core/SyntaxErrorException.cs index 21744d15d..37cc29add 100644 --- a/YamlDotNet/Core/SyntaxErrorException.cs +++ b/YamlDotNet/Core/SyntaxErrorException.cs @@ -1,58 +1,58 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - /// - /// Exception that is thrown when a syntax error is detected on a YAML stream. - /// - public sealed class SyntaxErrorException : YamlException - { - /// - /// Initializes a new instance of the class. - /// - /// The message. - public SyntaxErrorException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class. - /// - public SyntaxErrorException(in Mark start, in Mark end, string message) - : base(start, end, message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner. - public SyntaxErrorException(string message, Exception inner) - : base(message, inner) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + /// + /// Exception that is thrown when a syntax error is detected on a YAML stream. + /// + public sealed class SyntaxErrorException : YamlException + { + /// + /// Initializes a new instance of the class. + /// + /// The message. + public SyntaxErrorException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + public SyntaxErrorException(in Mark start, in Mark end, string message) + : base(start, end, message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner. + public SyntaxErrorException(string message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/YamlDotNet/Core/TagDirectiveCollection.cs b/YamlDotNet/Core/TagDirectiveCollection.cs index 980ec01e1..7c79a4a73 100644 --- a/YamlDotNet/Core/TagDirectiveCollection.cs +++ b/YamlDotNet/Core/TagDirectiveCollection.cs @@ -1,63 +1,63 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using System.Collections.ObjectModel; -using YamlDotNet.Core.Tokens; - -namespace YamlDotNet.Core -{ - /// - /// Collection of . - /// - public sealed class TagDirectiveCollection : KeyedCollection - { - /// - /// Initializes a new instance of the class. - /// - public TagDirectiveCollection() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Initial content of the collection. - public TagDirectiveCollection(IEnumerable tagDirectives) - { - foreach (var tagDirective in tagDirectives) - { - Add(tagDirective); - } - } - - /// - protected override string GetKeyForItem(TagDirective item) => item.Handle; - - /// - /// Gets a value indicating whether the collection contains a directive with the same handle - /// - public new bool Contains(TagDirective directive) - { - return Contains(GetKeyForItem(directive)); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using YamlDotNet.Core.Tokens; + +namespace YamlDotNet.Core +{ + /// + /// Collection of . + /// + public sealed class TagDirectiveCollection : KeyedCollection + { + /// + /// Initializes a new instance of the class. + /// + public TagDirectiveCollection() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Initial content of the collection. + public TagDirectiveCollection(IEnumerable tagDirectives) + { + foreach (var tagDirective in tagDirectives) + { + Add(tagDirective); + } + } + + /// + protected override string GetKeyForItem(TagDirective item) => item.Handle; + + /// + /// Gets a value indicating whether the collection contains a directive with the same handle + /// + public new bool Contains(TagDirective directive) + { + return Contains(GetKeyForItem(directive)); + } + } +} diff --git a/YamlDotNet/Core/TagName.cs b/YamlDotNet/Core/TagName.cs index 17b89e583..458841e9b 100644 --- a/YamlDotNet/Core/TagName.cs +++ b/YamlDotNet/Core/TagName.cs @@ -1,91 +1,91 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - public readonly struct TagName : IEquatable - { - public static readonly TagName Empty = default; - - private readonly string? value; - - public string Value => value ?? throw new InvalidOperationException("Cannot read the Value of a non-specific tag"); - - public bool IsEmpty => value is null; - public bool IsNonSpecific => !IsEmpty && (value == "!" || value == "?"); - - public bool IsLocal => !IsEmpty && Value[0] == '!'; - public bool IsGlobal => !IsEmpty && !IsLocal; - - public TagName(string value) - { - this.value = value ?? throw new ArgumentNullException(nameof(value)); - - if (value.Length == 0) - { - throw new ArgumentException("Tag value must not be empty.", nameof(value)); - } - - if (IsGlobal && !Uri.IsWellFormedUriString(value, UriKind.RelativeOrAbsolute)) - { - throw new ArgumentException("Global tags must be valid URIs.", nameof(value)); - } - } - - public override string ToString() => value ?? "?"; - - public bool Equals(TagName other) => Equals(value, other.value); - - public override bool Equals(object? obj) - { - return obj is TagName other && Equals(other); - } - - public override int GetHashCode() - { - return value?.GetHashCode() ?? 0; - } - - public static bool operator ==(TagName left, TagName right) - { - return left.Equals(right); - } - - public static bool operator !=(TagName left, TagName right) - { - return !(left == right); - } - - public static bool operator ==(TagName left, string right) - { - return Equals(left.value, right); - } - - public static bool operator !=(TagName left, string right) - { - return !(left == right); - } - - public static implicit operator TagName(string? value) => value == null ? Empty : new TagName(value); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + public readonly struct TagName : IEquatable + { + public static readonly TagName Empty = default; + + private readonly string? value; + + public string Value => value ?? throw new InvalidOperationException("Cannot read the Value of a non-specific tag"); + + public bool IsEmpty => value is null; + public bool IsNonSpecific => !IsEmpty && (value == "!" || value == "?"); + + public bool IsLocal => !IsEmpty && Value[0] == '!'; + public bool IsGlobal => !IsEmpty && !IsLocal; + + public TagName(string value) + { + this.value = value ?? throw new ArgumentNullException(nameof(value)); + + if (value.Length == 0) + { + throw new ArgumentException("Tag value must not be empty.", nameof(value)); + } + + if (IsGlobal && !Uri.IsWellFormedUriString(value, UriKind.RelativeOrAbsolute)) + { + throw new ArgumentException("Global tags must be valid URIs.", nameof(value)); + } + } + + public override string ToString() => value ?? "?"; + + public bool Equals(TagName other) => Equals(value, other.value); + + public override bool Equals(object? obj) + { + return obj is TagName other && Equals(other); + } + + public override int GetHashCode() + { + return value?.GetHashCode() ?? 0; + } + + public static bool operator ==(TagName left, TagName right) + { + return left.Equals(right); + } + + public static bool operator !=(TagName left, TagName right) + { + return !(left == right); + } + + public static bool operator ==(TagName left, string right) + { + return Equals(left.value, right); + } + + public static bool operator !=(TagName left, string right) + { + return !(left == right); + } + + public static implicit operator TagName(string? value) => value == null ? Empty : new TagName(value); + } +} diff --git a/YamlDotNet/Core/Tokens/Anchor.cs b/YamlDotNet/Core/Tokens/Anchor.cs index 8a84e4522..630211c32 100644 --- a/YamlDotNet/Core/Tokens/Anchor.cs +++ b/YamlDotNet/Core/Tokens/Anchor.cs @@ -1,62 +1,62 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents an anchor token. - /// - public class Anchor : Token - { - /// - /// Gets the value. - /// - /// The value. - public AnchorName Value { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The value. - public Anchor(AnchorName value) - : this(value, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The value. - /// The start position of the token. - /// The end position of the token. - public Anchor(AnchorName value, Mark start, Mark end) - : base(start, end) - { - if (value.IsEmpty) - { - throw new ArgumentNullException(nameof(value)); - } - this.Value = value; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents an anchor token. + /// + public class Anchor : Token + { + /// + /// Gets the value. + /// + /// The value. + public AnchorName Value { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public Anchor(AnchorName value) + : this(value, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + /// The start position of the token. + /// The end position of the token. + public Anchor(AnchorName value, Mark start, Mark end) + : base(start, end) + { + if (value.IsEmpty) + { + throw new ArgumentNullException(nameof(value)); + } + this.Value = value; + } + } +} diff --git a/YamlDotNet/Core/Tokens/AnchorAlias.cs b/YamlDotNet/Core/Tokens/AnchorAlias.cs index b2ee14dec..d542d817c 100644 --- a/YamlDotNet/Core/Tokens/AnchorAlias.cs +++ b/YamlDotNet/Core/Tokens/AnchorAlias.cs @@ -1,61 +1,61 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents an alias token. - /// - public sealed class AnchorAlias : Token - { - /// - /// Gets the value of the alias. - /// - public AnchorName Value { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The value of the anchor. - public AnchorAlias(AnchorName value) - : this(value, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The value of the anchor. - /// The start position of the event. - /// The end position of the event. - public AnchorAlias(AnchorName value, Mark start, Mark end) - : base(start, end) - { - if (value.IsEmpty) - { - throw new ArgumentNullException(nameof(value)); - } - this.Value = value; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents an alias token. + /// + public sealed class AnchorAlias : Token + { + /// + /// Gets the value of the alias. + /// + public AnchorName Value { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The value of the anchor. + public AnchorAlias(AnchorName value) + : this(value, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value of the anchor. + /// The start position of the event. + /// The end position of the event. + public AnchorAlias(AnchorName value, Mark start, Mark end) + : base(start, end) + { + if (value.IsEmpty) + { + throw new ArgumentNullException(nameof(value)); + } + this.Value = value; + } + } +} diff --git a/YamlDotNet/Core/Tokens/BlockEnd.cs b/YamlDotNet/Core/Tokens/BlockEnd.cs index b0252180c..691c9361b 100644 --- a/YamlDotNet/Core/Tokens/BlockEnd.cs +++ b/YamlDotNet/Core/Tokens/BlockEnd.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a block end token. - /// - public sealed class BlockEnd : Token - { - /// - /// Initializes a new instance of the class. - /// - public BlockEnd() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public BlockEnd(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a block end token. + /// + public sealed class BlockEnd : Token + { + /// + /// Initializes a new instance of the class. + /// + public BlockEnd() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public BlockEnd(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/BlockEntry.cs b/YamlDotNet/Core/Tokens/BlockEntry.cs index 6f36b4e44..159f5de76 100644 --- a/YamlDotNet/Core/Tokens/BlockEntry.cs +++ b/YamlDotNet/Core/Tokens/BlockEntry.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a block entry event. - /// - public sealed class BlockEntry : Token - { - /// - /// Initializes a new instance of the class. - /// - public BlockEntry() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public BlockEntry(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a block entry event. + /// + public sealed class BlockEntry : Token + { + /// + /// Initializes a new instance of the class. + /// + public BlockEntry() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public BlockEntry(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/BlockMappingStart.cs b/YamlDotNet/Core/Tokens/BlockMappingStart.cs index edec2e962..be705ad3b 100644 --- a/YamlDotNet/Core/Tokens/BlockMappingStart.cs +++ b/YamlDotNet/Core/Tokens/BlockMappingStart.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a block mapping start token. - /// - public sealed class BlockMappingStart : Token - { - /// - /// Initializes a new instance of the class. - /// - public BlockMappingStart() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public BlockMappingStart(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a block mapping start token. + /// + public sealed class BlockMappingStart : Token + { + /// + /// Initializes a new instance of the class. + /// + public BlockMappingStart() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public BlockMappingStart(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/BlockSequenceStart.cs b/YamlDotNet/Core/Tokens/BlockSequenceStart.cs index a4157cddb..d27adc4a2 100644 --- a/YamlDotNet/Core/Tokens/BlockSequenceStart.cs +++ b/YamlDotNet/Core/Tokens/BlockSequenceStart.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a block sequence start token. - /// - public sealed class BlockSequenceStart : Token - { - /// - /// Initializes a new instance of the class. - /// - public BlockSequenceStart() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public BlockSequenceStart(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a block sequence start token. + /// + public sealed class BlockSequenceStart : Token + { + /// + /// Initializes a new instance of the class. + /// + public BlockSequenceStart() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public BlockSequenceStart(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/Comment.cs b/YamlDotNet/Core/Tokens/Comment.cs index b12033732..9d77b19f3 100644 --- a/YamlDotNet/Core/Tokens/Comment.cs +++ b/YamlDotNet/Core/Tokens/Comment.cs @@ -1,59 +1,59 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a comment - /// - public sealed class Comment : Token - { - /// - /// Gets the value of the comment - /// - public string Value { get; } - - /// - /// Gets a value indicating whether the comment appears other tokens on that line. - /// - public bool IsInline { get; } - - /// - /// Initializes a new instance of the class. - /// - public Comment(string value, bool isInline) - : this(value, isInline, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - public Comment(string value, bool isInline, Mark start, Mark end) - : base(start, end) - { - Value = value ?? throw new ArgumentNullException(nameof(value)); - IsInline = isInline; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a comment + /// + public sealed class Comment : Token + { + /// + /// Gets the value of the comment + /// + public string Value { get; } + + /// + /// Gets a value indicating whether the comment appears other tokens on that line. + /// + public bool IsInline { get; } + + /// + /// Initializes a new instance of the class. + /// + public Comment(string value, bool isInline) + : this(value, isInline, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + public Comment(string value, bool isInline, Mark start, Mark end) + : base(start, end) + { + Value = value ?? throw new ArgumentNullException(nameof(value)); + IsInline = isInline; + } + } +} diff --git a/YamlDotNet/Core/Tokens/DocumentEnd.cs b/YamlDotNet/Core/Tokens/DocumentEnd.cs index d1892522b..4167cba2e 100644 --- a/YamlDotNet/Core/Tokens/DocumentEnd.cs +++ b/YamlDotNet/Core/Tokens/DocumentEnd.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a document end token. - /// - public sealed class DocumentEnd : Token - { - /// - /// Initializes a new instance of the class. - /// - public DocumentEnd() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public DocumentEnd(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a document end token. + /// + public sealed class DocumentEnd : Token + { + /// + /// Initializes a new instance of the class. + /// + public DocumentEnd() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public DocumentEnd(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/DocumentStart.cs b/YamlDotNet/Core/Tokens/DocumentStart.cs index 0fe3ad83f..347844d5c 100644 --- a/YamlDotNet/Core/Tokens/DocumentStart.cs +++ b/YamlDotNet/Core/Tokens/DocumentStart.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a document start token. - /// - public sealed class DocumentStart : Token - { - /// - /// Initializes a new instance of the class. - /// - public DocumentStart() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public DocumentStart(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a document start token. + /// + public sealed class DocumentStart : Token + { + /// + /// Initializes a new instance of the class. + /// + public DocumentStart() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public DocumentStart(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/Error.cs b/YamlDotNet/Core/Tokens/Error.cs index f835c9673..1f924f5a1 100644 --- a/YamlDotNet/Core/Tokens/Error.cs +++ b/YamlDotNet/Core/Tokens/Error.cs @@ -1,40 +1,40 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Base class for YAML tokens. - /// - internal class Error : Token - { - /// - /// Gets the value of the comment - /// - internal string Value { get; } - - internal Error(string value, Mark start, Mark end) - : base(start, end) - { - Value = value; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Base class for YAML tokens. + /// + internal class Error : Token + { + /// + /// Gets the value of the comment + /// + internal string Value { get; } + + internal Error(string value, Mark start, Mark end) + : base(start, end) + { + Value = value; + } + } +} diff --git a/YamlDotNet/Core/Tokens/FlowEntry.cs b/YamlDotNet/Core/Tokens/FlowEntry.cs index 103334e95..a0260a99d 100644 --- a/YamlDotNet/Core/Tokens/FlowEntry.cs +++ b/YamlDotNet/Core/Tokens/FlowEntry.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a flow entry event. - /// - public sealed class FlowEntry : Token - { - /// - /// Initializes a new instance of the class. - /// - public FlowEntry() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public FlowEntry(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a flow entry event. + /// + public sealed class FlowEntry : Token + { + /// + /// Initializes a new instance of the class. + /// + public FlowEntry() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public FlowEntry(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/FlowMappingEnd.cs b/YamlDotNet/Core/Tokens/FlowMappingEnd.cs index 23770b3f0..eef414b8b 100644 --- a/YamlDotNet/Core/Tokens/FlowMappingEnd.cs +++ b/YamlDotNet/Core/Tokens/FlowMappingEnd.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a flow mapping end token. - /// - public sealed class FlowMappingEnd : Token - { - /// - /// Initializes a new instance of the class. - /// - public FlowMappingEnd() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public FlowMappingEnd(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a flow mapping end token. + /// + public sealed class FlowMappingEnd : Token + { + /// + /// Initializes a new instance of the class. + /// + public FlowMappingEnd() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public FlowMappingEnd(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/FlowMappingStart.cs b/YamlDotNet/Core/Tokens/FlowMappingStart.cs index c75d070ef..771a692cb 100644 --- a/YamlDotNet/Core/Tokens/FlowMappingStart.cs +++ b/YamlDotNet/Core/Tokens/FlowMappingStart.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a flow mapping start token. - /// - public sealed class FlowMappingStart : Token - { - /// - /// Initializes a new instance of the class. - /// - public FlowMappingStart() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public FlowMappingStart(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a flow mapping start token. + /// + public sealed class FlowMappingStart : Token + { + /// + /// Initializes a new instance of the class. + /// + public FlowMappingStart() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public FlowMappingStart(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/FlowSequenceEnd.cs b/YamlDotNet/Core/Tokens/FlowSequenceEnd.cs index f7deaf658..d554c6032 100644 --- a/YamlDotNet/Core/Tokens/FlowSequenceEnd.cs +++ b/YamlDotNet/Core/Tokens/FlowSequenceEnd.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a flow sequence end token. - /// - public sealed class FlowSequenceEnd : Token - { - /// - /// Initializes a new instance of the class. - /// - public FlowSequenceEnd() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public FlowSequenceEnd(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a flow sequence end token. + /// + public sealed class FlowSequenceEnd : Token + { + /// + /// Initializes a new instance of the class. + /// + public FlowSequenceEnd() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public FlowSequenceEnd(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/FlowSequenceStart.cs b/YamlDotNet/Core/Tokens/FlowSequenceStart.cs index 35080ace7..5fc04ed9d 100644 --- a/YamlDotNet/Core/Tokens/FlowSequenceStart.cs +++ b/YamlDotNet/Core/Tokens/FlowSequenceStart.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a flow sequence start token. - /// - public sealed class FlowSequenceStart : Token - { - /// - /// Initializes a new instance of the class. - /// - public FlowSequenceStart() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public FlowSequenceStart(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a flow sequence start token. + /// + public sealed class FlowSequenceStart : Token + { + /// + /// Initializes a new instance of the class. + /// + public FlowSequenceStart() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public FlowSequenceStart(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/Key.cs b/YamlDotNet/Core/Tokens/Key.cs index fba6896f6..87c0442ab 100644 --- a/YamlDotNet/Core/Tokens/Key.cs +++ b/YamlDotNet/Core/Tokens/Key.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a key token. - /// - public sealed class Key : Token - { - /// - /// Initializes a new instance of the class. - /// - public Key() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public Key(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a key token. + /// + public sealed class Key : Token + { + /// + /// Initializes a new instance of the class. + /// + public Key() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public Key(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/Scalar.cs b/YamlDotNet/Core/Tokens/Scalar.cs index 6dee8bff7..f8daa7b25 100644 --- a/YamlDotNet/Core/Tokens/Scalar.cs +++ b/YamlDotNet/Core/Tokens/Scalar.cs @@ -1,82 +1,82 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - - -using System; - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a scalar token. - /// - public sealed class Scalar : Token - { - /// - /// Gets or sets whether this scalar is a key - /// - public bool IsKey { get; set; } - - /// - /// Gets the value. - /// - /// The value. - public string Value { get; } - - /// - /// Gets the style. - /// - /// The style. - public ScalarStyle Style { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The value. - public Scalar(string value) - : this(value, ScalarStyle.Any) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The value. - /// The style. - public Scalar(string value, ScalarStyle style) - : this(value, style, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The value. - /// The style. - /// The start position of the token. - /// The end position of the token. - public Scalar(string value, ScalarStyle style, Mark start, Mark end) - : base(start, end) - { - this.Value = value ?? throw new ArgumentNullException(nameof(value)); - this.Style = style; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +using System; + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a scalar token. + /// + public sealed class Scalar : Token + { + /// + /// Gets or sets whether this scalar is a key + /// + public bool IsKey { get; set; } + + /// + /// Gets the value. + /// + /// The value. + public string Value { get; } + + /// + /// Gets the style. + /// + /// The style. + public ScalarStyle Style { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public Scalar(string value) + : this(value, ScalarStyle.Any) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + /// The style. + public Scalar(string value, ScalarStyle style) + : this(value, style, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + /// The style. + /// The start position of the token. + /// The end position of the token. + public Scalar(string value, ScalarStyle style, Mark start, Mark end) + : base(start, end) + { + this.Value = value ?? throw new ArgumentNullException(nameof(value)); + this.Style = style; + } + } +} diff --git a/YamlDotNet/Core/Tokens/StreamEnd.cs b/YamlDotNet/Core/Tokens/StreamEnd.cs index 3169056d4..934c1d25e 100644 --- a/YamlDotNet/Core/Tokens/StreamEnd.cs +++ b/YamlDotNet/Core/Tokens/StreamEnd.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a stream end event. - /// - public sealed class StreamEnd : Token - { - /// - /// Initializes a new instance of the class. - /// - public StreamEnd() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public StreamEnd(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a stream end event. + /// + public sealed class StreamEnd : Token + { + /// + /// Initializes a new instance of the class. + /// + public StreamEnd() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public StreamEnd(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/StreamStart.cs b/YamlDotNet/Core/Tokens/StreamStart.cs index 6a8194cde..e992a647e 100644 --- a/YamlDotNet/Core/Tokens/StreamStart.cs +++ b/YamlDotNet/Core/Tokens/StreamStart.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a stream start token. - /// - public sealed class StreamStart : Token - { - /// - /// Initializes a new instance of the class. - /// - public StreamStart() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public StreamStart(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a stream start token. + /// + public sealed class StreamStart : Token + { + /// + /// Initializes a new instance of the class. + /// + public StreamStart() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public StreamStart(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/Tag.cs b/YamlDotNet/Core/Tokens/Tag.cs index 630ef9836..edf698c2b 100644 --- a/YamlDotNet/Core/Tokens/Tag.cs +++ b/YamlDotNet/Core/Tokens/Tag.cs @@ -1,67 +1,67 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a tag token. - /// - public sealed class Tag : Token - { - /// - /// Gets the handle. - /// - /// The handle. - public string Handle { get; } - - /// - /// Gets the suffix. - /// - /// The suffix. - public string Suffix { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The handle. - /// The suffix. - public Tag(string handle, string suffix) - : this(handle, suffix, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The handle. - /// The suffix. - /// The start position of the token. - /// The end position of the token. - public Tag(string handle, string suffix, Mark start, Mark end) - : base(start, end) - { - this.Handle = handle ?? throw new ArgumentNullException(nameof(handle)); - this.Suffix = suffix ?? throw new ArgumentNullException(nameof(suffix)); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a tag token. + /// + public sealed class Tag : Token + { + /// + /// Gets the handle. + /// + /// The handle. + public string Handle { get; } + + /// + /// Gets the suffix. + /// + /// The suffix. + public string Suffix { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The handle. + /// The suffix. + public Tag(string handle, string suffix) + : this(handle, suffix, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The handle. + /// The suffix. + /// The start position of the token. + /// The end position of the token. + public Tag(string handle, string suffix, Mark start, Mark end) + : base(start, end) + { + this.Handle = handle ?? throw new ArgumentNullException(nameof(handle)); + this.Suffix = suffix ?? throw new ArgumentNullException(nameof(suffix)); + } + } +} diff --git a/YamlDotNet/Core/Tokens/TagDirective.cs b/YamlDotNet/Core/Tokens/TagDirective.cs index db20c21e5..0ee08e9a6 100644 --- a/YamlDotNet/Core/Tokens/TagDirective.cs +++ b/YamlDotNet/Core/Tokens/TagDirective.cs @@ -1,118 +1,118 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Text.RegularExpressions; - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a tag directive token. - /// - public class TagDirective : Token - { - - /// - /// Gets the handle. - /// - /// The handle. - public string Handle { get; } - - /// - /// Gets the prefix. - /// - /// The prefix. - public string Prefix { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The handle. - /// The prefix. - public TagDirective(string handle, string prefix) - : this(handle, prefix, Mark.Empty, Mark.Empty) - { - } - - private static readonly Regex TagHandlePattern = new Regex(@"^!([0-9A-Za-z_\-]*!)?$", StandardRegexOptions.Compiled); - - /// - /// Initializes a new instance of the class. - /// - /// The handle. - /// The prefix. - /// The start position of the token. - /// The end position of the token. - public TagDirective(string handle, string prefix, Mark start, Mark end) - : base(start, end) - { - if (string.IsNullOrEmpty(handle)) - { - throw new ArgumentNullException(nameof(handle), "Tag handle must not be empty."); - } - - if (!TagHandlePattern.IsMatch(handle)) - { - throw new ArgumentException("Tag handle must start and end with '!' and contain alphanumerical characters only.", nameof(handle)); - } - - this.Handle = handle; - - if (string.IsNullOrEmpty(prefix)) - { - throw new ArgumentNullException(nameof(prefix), "Tag prefix must not be empty."); - } - - this.Prefix = prefix; - } - - /// - /// Determines whether the specified System.Object is equal to the current System.Object. - /// - /// The System.Object to compare with the current System.Object. - /// - /// true if the specified System.Object is equal to the current System.Object; otherwise, false. - /// - public override bool Equals(object? obj) - { - return obj is TagDirective other - && Handle.Equals(other.Handle) - && Prefix.Equals(other.Prefix); - } - - /// - /// Serves as a hash function for a particular type. - /// - /// - /// A hash code for the current . - /// - public override int GetHashCode() - { - return Handle.GetHashCode() ^ Prefix.GetHashCode(); - } - - /// - public override string ToString() - { - return $"{Handle} => {Prefix}"; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Text.RegularExpressions; + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a tag directive token. + /// + public class TagDirective : Token + { + + /// + /// Gets the handle. + /// + /// The handle. + public string Handle { get; } + + /// + /// Gets the prefix. + /// + /// The prefix. + public string Prefix { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The handle. + /// The prefix. + public TagDirective(string handle, string prefix) + : this(handle, prefix, Mark.Empty, Mark.Empty) + { + } + + private static readonly Regex TagHandlePattern = new Regex(@"^!([0-9A-Za-z_\-]*!)?$", StandardRegexOptions.Compiled); + + /// + /// Initializes a new instance of the class. + /// + /// The handle. + /// The prefix. + /// The start position of the token. + /// The end position of the token. + public TagDirective(string handle, string prefix, Mark start, Mark end) + : base(start, end) + { + if (string.IsNullOrEmpty(handle)) + { + throw new ArgumentNullException(nameof(handle), "Tag handle must not be empty."); + } + + if (!TagHandlePattern.IsMatch(handle)) + { + throw new ArgumentException("Tag handle must start and end with '!' and contain alphanumerical characters only.", nameof(handle)); + } + + this.Handle = handle; + + if (string.IsNullOrEmpty(prefix)) + { + throw new ArgumentNullException(nameof(prefix), "Tag prefix must not be empty."); + } + + this.Prefix = prefix; + } + + /// + /// Determines whether the specified System.Object is equal to the current System.Object. + /// + /// The System.Object to compare with the current System.Object. + /// + /// true if the specified System.Object is equal to the current System.Object; otherwise, false. + /// + public override bool Equals(object? obj) + { + return obj is TagDirective other + && Handle.Equals(other.Handle) + && Prefix.Equals(other.Prefix); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return Handle.GetHashCode() ^ Prefix.GetHashCode(); + } + + /// + public override string ToString() + { + return $"{Handle} => {Prefix}"; + } + } +} diff --git a/YamlDotNet/Core/Tokens/Token.cs b/YamlDotNet/Core/Tokens/Token.cs index d4cb81feb..2fa4492fe 100644 --- a/YamlDotNet/Core/Tokens/Token.cs +++ b/YamlDotNet/Core/Tokens/Token.cs @@ -1,50 +1,50 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Base class for YAML tokens. - /// - public abstract class Token - { - /// - /// Gets the start of the token in the input stream. - /// - public Mark Start { get; } - - /// - /// Gets the end of the token in the input stream. - /// - public Mark End { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - protected Token(in Mark start, in Mark end) - { - this.Start = start; - this.End = end; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Base class for YAML tokens. + /// + public abstract class Token + { + /// + /// Gets the start of the token in the input stream. + /// + public Mark Start { get; } + + /// + /// Gets the end of the token in the input stream. + /// + public Mark End { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + protected Token(in Mark start, in Mark end) + { + this.Start = start; + this.End = end; + } + } +} diff --git a/YamlDotNet/Core/Tokens/Value.cs b/YamlDotNet/Core/Tokens/Value.cs index ab8dfd8e9..7e1ab659c 100644 --- a/YamlDotNet/Core/Tokens/Value.cs +++ b/YamlDotNet/Core/Tokens/Value.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a value token. - /// - public sealed class Value : Token - { - /// - /// Initializes a new instance of the class. - /// - public Value() - : this(Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The start position of the token. - /// The end position of the token. - public Value(in Mark start, in Mark end) - : base(start, end) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a value token. + /// + public sealed class Value : Token + { + /// + /// Initializes a new instance of the class. + /// + public Value() + : this(Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The start position of the token. + /// The end position of the token. + public Value(in Mark start, in Mark end) + : base(start, end) + { + } + } +} diff --git a/YamlDotNet/Core/Tokens/VersionDirective.cs b/YamlDotNet/Core/Tokens/VersionDirective.cs index 1a999a65c..4b648ca21 100644 --- a/YamlDotNet/Core/Tokens/VersionDirective.cs +++ b/YamlDotNet/Core/Tokens/VersionDirective.cs @@ -1,80 +1,80 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Core.Tokens -{ - /// - /// Represents a version directive token. - /// - public sealed class VersionDirective : Token - { - /// - /// Gets the version. - /// - /// The version. - public Version Version { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The version. - public VersionDirective(Version version) - : this(version, Mark.Empty, Mark.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The version. - /// The start position of the token. - /// The end position of the token. - public VersionDirective(Version version, Mark start, Mark end) - : base(start, end) - { - this.Version = version; - } - - /// - /// Determines whether the specified System.Object is equal to the current System.Object. - /// - /// The System.Object to compare with the current System.Object. - /// - /// true if the specified System.Object is equal to the current System.Object; otherwise, false. - /// - public override bool Equals(object? obj) - { - return obj is VersionDirective other - && Version.Equals(other.Version); - } - - /// - /// Serves as a hash function for a particular type. - /// - /// - /// A hash code for the current . - /// - public override int GetHashCode() - { - return Version.GetHashCode(); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Core.Tokens +{ + /// + /// Represents a version directive token. + /// + public sealed class VersionDirective : Token + { + /// + /// Gets the version. + /// + /// The version. + public Version Version { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The version. + public VersionDirective(Version version) + : this(version, Mark.Empty, Mark.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The version. + /// The start position of the token. + /// The end position of the token. + public VersionDirective(Version version, Mark start, Mark end) + : base(start, end) + { + this.Version = version; + } + + /// + /// Determines whether the specified System.Object is equal to the current System.Object. + /// + /// The System.Object to compare with the current System.Object. + /// + /// true if the specified System.Object is equal to the current System.Object; otherwise, false. + /// + public override bool Equals(object? obj) + { + return obj is VersionDirective other + && Version.Equals(other.Version); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return Version.GetHashCode(); + } + } +} diff --git a/YamlDotNet/Core/Version.cs b/YamlDotNet/Core/Version.cs index 80e8c9cee..4a93c6e15 100644 --- a/YamlDotNet/Core/Version.cs +++ b/YamlDotNet/Core/Version.cs @@ -1,82 +1,82 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - /// - /// Specifies the version of the YAML language. - /// - public sealed class Version - { - /// - /// Gets the major version number. - /// - public int Major { get; } - - /// - /// Gets the minor version number. - /// - public int Minor { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The major version number. - /// The minor version number. - public Version(int major, int minor) - { - Major = major >= 0 - ? major - : throw new ArgumentOutOfRangeException(nameof(major), $"{major} should be >= 0"); - - Minor = minor >= 0 - ? minor - : throw new ArgumentOutOfRangeException(nameof(minor), $"{minor} should be >= 0"); - } - - /// - /// Determines whether the specified System.Object is equal to the current System.Object. - /// - /// The System.Object to compare with the current System.Object. - /// - /// true if the specified System.Object is equal to the current System.Object; otherwise, false. - /// - public override bool Equals(object? obj) - { - return obj is Version other - && Major == other.Major - && Minor == other.Minor; - } - - /// - /// Serves as a hash function for a particular type. - /// - /// - /// A hash code for the current . - /// - public override int GetHashCode() - { - return HashCode.CombineHashCodes(Major.GetHashCode(), Minor.GetHashCode()); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + /// + /// Specifies the version of the YAML language. + /// + public sealed class Version + { + /// + /// Gets the major version number. + /// + public int Major { get; } + + /// + /// Gets the minor version number. + /// + public int Minor { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The major version number. + /// The minor version number. + public Version(int major, int minor) + { + Major = major >= 0 + ? major + : throw new ArgumentOutOfRangeException(nameof(major), $"{major} should be >= 0"); + + Minor = minor >= 0 + ? minor + : throw new ArgumentOutOfRangeException(nameof(minor), $"{minor} should be >= 0"); + } + + /// + /// Determines whether the specified System.Object is equal to the current System.Object. + /// + /// The System.Object to compare with the current System.Object. + /// + /// true if the specified System.Object is equal to the current System.Object; otherwise, false. + /// + public override bool Equals(object? obj) + { + return obj is Version other + && Major == other.Major + && Minor == other.Minor; + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return HashCode.CombineHashCodes(Major.GetHashCode(), Minor.GetHashCode()); + } + } +} diff --git a/YamlDotNet/Core/YamlException.cs b/YamlDotNet/Core/YamlException.cs index 7f24feb1b..ffe1f5fe6 100644 --- a/YamlDotNet/Core/YamlException.cs +++ b/YamlDotNet/Core/YamlException.cs @@ -1,83 +1,83 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Core -{ - /// - /// Base exception that is thrown when the a problem occurs in the YamlDotNet library. - /// - public class YamlException : Exception - { - /// - /// Gets the position in the input stream where the event that originated the exception starts. - /// - public Mark Start { get; } - - /// - /// Gets the position in the input stream where the event that originated the exception ends. - /// - public Mark End { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - public YamlException(string message) - : this(Mark.Empty, Mark.Empty, message) - { - } - - /// - /// Initializes a new instance of the class. - /// - public YamlException(in Mark start, in Mark end, string message) - : this(start, end, message, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - public YamlException(in Mark start, in Mark end, string message, Exception? innerException) - : base(message, innerException) - { - Start = start; - End = end; - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner. - public YamlException(string message, Exception inner) - : this(Mark.Empty, Mark.Empty, message, inner) - { - } - - public override string ToString() - { - return $"({Start}) - ({End}): {Message}"; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Core +{ + /// + /// Base exception that is thrown when the a problem occurs in the YamlDotNet library. + /// + public class YamlException : Exception + { + /// + /// Gets the position in the input stream where the event that originated the exception starts. + /// + public Mark Start { get; } + + /// + /// Gets the position in the input stream where the event that originated the exception ends. + /// + public Mark End { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public YamlException(string message) + : this(Mark.Empty, Mark.Empty, message) + { + } + + /// + /// Initializes a new instance of the class. + /// + public YamlException(in Mark start, in Mark end, string message) + : this(start, end, message, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + public YamlException(in Mark start, in Mark end, string message, Exception? innerException) + : base(message, innerException) + { + Start = start; + End = end; + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner. + public YamlException(string message, Exception inner) + : this(Mark.Empty, Mark.Empty, message, inner) + { + } + + public override string ToString() + { + return $"({Start}) - ({End}): {Message}"; + } + } +} diff --git a/YamlDotNet/GlobalSuppressions.cs b/YamlDotNet/GlobalSuppressions.cs index 3ae840c6b..2ecfc559f 100644 --- a/YamlDotNet/GlobalSuppressions.cs +++ b/YamlDotNet/GlobalSuppressions.cs @@ -1,27 +1,27 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0056:Use index operator", Justification = "Not available on older platforms", Scope = "member", Target = "~M:YamlDotNet.Core.Scanner.ScanTag~YamlDotNet.Core.Tokens.Token")] +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0056:Use index operator", Justification = "Not available on older platforms", Scope = "member", Target = "~M:YamlDotNet.Core.Scanner.ScanTag~YamlDotNet.Core.Tokens.Token")] diff --git a/YamlDotNet/Helpers/ConcurrentObjectPool.cs b/YamlDotNet/Helpers/ConcurrentObjectPool.cs index b1bac4ca3..9d3f66b35 100644 --- a/YamlDotNet/Helpers/ConcurrentObjectPool.cs +++ b/YamlDotNet/Helpers/ConcurrentObjectPool.cs @@ -1,199 +1,199 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - - -// Adapter from Microsoft code which is -// Copyright (c) Microsoft. -// All Rights Reserved. Licensed under the Apache License, Version 2.0. - -using System; -using System.Diagnostics; -using System.Threading; - -namespace YamlDotNet.Helpers -{ - /// - /// Generic implementation of object pooling pattern with predefined pool size limit. The main - /// purpose is that limited number of frequently used objects can be kept in the pool for - /// further recycling. - /// - /// Notes: - /// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there - /// is no space in the pool, extra returned objects will be dropped. - /// - /// 2) it is implied that if object was obtained from a pool, the caller will return it back in - /// a relatively short time. Keeping checked out objects for long durations is ok, but - /// reduces usefulness of pooling. Just new up your own. - /// - /// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. - /// Rationale: - /// If there is no intent for reusing the object, do not use pool - just use "new". - /// - [DebuggerStepThrough] - internal sealed class ConcurrentObjectPool where T : class - { - [DebuggerDisplay("{value,nq}")] - private struct Element - { - internal T? value; - } - - /// - /// Not using System.Func{T} because this file is linked into the (debugger) Formatter, - /// which does not have that type (since it compiles against .NET 2.0). - /// - internal delegate T Factory(); - - // Storage for the pool objects. The first item is stored in a dedicated field because we - // expect to be able to satisfy most requests from it. - private T? firstItem; - private readonly Element[] items; - - // factory is stored for the lifetime of the pool. We will call this only when pool needs to - // expand. compared to "new T()", Func gives more flexibility to implementers and faster - // than "new T()". - private readonly Factory factory; - - internal ConcurrentObjectPool(Factory factory) - : this(factory, Environment.ProcessorCount * 2) - { - } - - internal ConcurrentObjectPool(Factory factory, int size) - { - Debug.Assert(size >= 1); - this.factory = factory; - items = new Element[size - 1]; - } - - private T CreateInstance() - { - var inst = factory(); - return inst; - } - - /// - /// Produces an instance. - /// - /// - /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. - /// Note that Free will try to store recycled objects close to the start thus statistically - /// reducing how far we will typically search. - /// - internal T Allocate() - { - // PERF: Examine the first element. If that fails, AllocateSlow will look at the remaining elements. - // Note that the initial read is optimistically not synchronized. That is intentional. - // We will interlock only when we have a candidate. in a worst case we may miss some - // recently returned objects. Not a big deal. - var inst = firstItem; - if (inst == null || inst != Interlocked.CompareExchange(ref firstItem, null, inst)) - { - inst = AllocateSlow(); - } - - return inst; - } - - private T AllocateSlow() - { - var elements = items; - - for (var i = 0; i < elements.Length; i++) - { - // Note that the initial read is optimistically not synchronized. That is intentional. - // We will interlock only when we have a candidate. in a worst case we may miss some - // recently returned objects. Not a big deal. - var inst = elements[i].value; - if (inst != null) - { - if (inst == Interlocked.CompareExchange(ref elements[i].value, null, inst)) - { - return inst; - } - } - } - - return CreateInstance(); - } - - /// - /// Returns objects to the pool. - /// - /// - /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. - /// Note that Free will try to store recycled objects close to the start thus statistically - /// reducing how far we will typically search in Allocate. - /// - internal void Free(T obj) - { - Validate(obj); - - if (firstItem == null) - { - // Intentionally not using interlocked here. - // In a worst case scenario two objects may be stored into same slot. - // It is very unlikely to happen and will only mean that one of the objects will get collected. - firstItem = obj; - } - else - { - FreeSlow(obj); - } - } - - private void FreeSlow(T obj) - { - var elements = items; - for (var i = 0; i < elements.Length; i++) - { - if (elements[i].value == null) - { - // Intentionally not using interlocked here. - // In a worst case scenario two objects may be stored into same slot. - // It is very unlikely to happen and will only mean that one of the objects will get collected. - elements[i].value = obj; - break; - } - } - } - - [Conditional("DEBUG")] - private void Validate(object obj) - { - Debug.Assert(obj != null, "freeing null?"); - - Debug.Assert(firstItem != obj, "freeing twice?"); - - var elements = items; - for (var i = 0; i < elements.Length; i++) - { - var value = elements[i].value; - if (value == null) - { - return; - } - - Debug.Assert(value != obj, "freeing twice?"); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +// Adapter from Microsoft code which is +// Copyright (c) Microsoft. +// All Rights Reserved. Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Threading; + +namespace YamlDotNet.Helpers +{ + /// + /// Generic implementation of object pooling pattern with predefined pool size limit. The main + /// purpose is that limited number of frequently used objects can be kept in the pool for + /// further recycling. + /// + /// Notes: + /// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there + /// is no space in the pool, extra returned objects will be dropped. + /// + /// 2) it is implied that if object was obtained from a pool, the caller will return it back in + /// a relatively short time. Keeping checked out objects for long durations is ok, but + /// reduces usefulness of pooling. Just new up your own. + /// + /// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. + /// Rationale: + /// If there is no intent for reusing the object, do not use pool - just use "new". + /// + [DebuggerStepThrough] + internal sealed class ConcurrentObjectPool where T : class + { + [DebuggerDisplay("{value,nq}")] + private struct Element + { + internal T? value; + } + + /// + /// Not using System.Func{T} because this file is linked into the (debugger) Formatter, + /// which does not have that type (since it compiles against .NET 2.0). + /// + internal delegate T Factory(); + + // Storage for the pool objects. The first item is stored in a dedicated field because we + // expect to be able to satisfy most requests from it. + private T? firstItem; + private readonly Element[] items; + + // factory is stored for the lifetime of the pool. We will call this only when pool needs to + // expand. compared to "new T()", Func gives more flexibility to implementers and faster + // than "new T()". + private readonly Factory factory; + + internal ConcurrentObjectPool(Factory factory) + : this(factory, Environment.ProcessorCount * 2) + { + } + + internal ConcurrentObjectPool(Factory factory, int size) + { + Debug.Assert(size >= 1); + this.factory = factory; + items = new Element[size - 1]; + } + + private T CreateInstance() + { + var inst = factory(); + return inst; + } + + /// + /// Produces an instance. + /// + /// + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search. + /// + internal T Allocate() + { + // PERF: Examine the first element. If that fails, AllocateSlow will look at the remaining elements. + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + var inst = firstItem; + if (inst == null || inst != Interlocked.CompareExchange(ref firstItem, null, inst)) + { + inst = AllocateSlow(); + } + + return inst; + } + + private T AllocateSlow() + { + var elements = items; + + for (var i = 0; i < elements.Length; i++) + { + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + var inst = elements[i].value; + if (inst != null) + { + if (inst == Interlocked.CompareExchange(ref elements[i].value, null, inst)) + { + return inst; + } + } + } + + return CreateInstance(); + } + + /// + /// Returns objects to the pool. + /// + /// + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search in Allocate. + /// + internal void Free(T obj) + { + Validate(obj); + + if (firstItem == null) + { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + firstItem = obj; + } + else + { + FreeSlow(obj); + } + } + + private void FreeSlow(T obj) + { + var elements = items; + for (var i = 0; i < elements.Length; i++) + { + if (elements[i].value == null) + { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + elements[i].value = obj; + break; + } + } + } + + [Conditional("DEBUG")] + private void Validate(object obj) + { + Debug.Assert(obj != null, "freeing null?"); + + Debug.Assert(firstItem != obj, "freeing twice?"); + + var elements = items; + for (var i = 0; i < elements.Length; i++) + { + var value = elements[i].value; + if (value == null) + { + return; + } + + Debug.Assert(value != obj, "freeing twice?"); + } + } + } +} diff --git a/YamlDotNet/Helpers/ExpressionExtensions.cs b/YamlDotNet/Helpers/ExpressionExtensions.cs index e345c09e8..73596f19a 100644 --- a/YamlDotNet/Helpers/ExpressionExtensions.cs +++ b/YamlDotNet/Helpers/ExpressionExtensions.cs @@ -1,85 +1,85 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !NET20 - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using System.Reflection; - -namespace YamlDotNet.Helpers -{ - public static class ExpressionExtensions - { - /// - /// Returns the that describes the property that - /// is being returned in an expression in the form: - /// - /// x => x.SomeProperty - /// - /// - public static PropertyInfo AsProperty(this LambdaExpression propertyAccessor) - { - var property = TryGetMemberExpression(propertyAccessor); - if (property == null) - { - throw new ArgumentException("Expected a lambda expression in the form: x => x.SomeProperty", nameof(propertyAccessor)); - } - - return property; - } - - [return: MaybeNull] - private static TMemberInfo TryGetMemberExpression(LambdaExpression lambdaExpression) - where TMemberInfo : MemberInfo - { - if (lambdaExpression.Parameters.Count != 1) - { - return null; - } - - var body = lambdaExpression.Body; - - if (body is UnaryExpression castExpression) - { - if (castExpression.NodeType != ExpressionType.Convert) - { - return null; - } - - body = castExpression.Operand; - } - - if (body is MemberExpression memberExpression) - { - if (memberExpression.Expression != lambdaExpression.Parameters[0]) - { - return null; - } - - return memberExpression.Member as TMemberInfo; - } - return null; - } - } -} -#endif +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if !NET20 + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Reflection; + +namespace YamlDotNet.Helpers +{ + public static class ExpressionExtensions + { + /// + /// Returns the that describes the property that + /// is being returned in an expression in the form: + /// + /// x => x.SomeProperty + /// + /// + public static PropertyInfo AsProperty(this LambdaExpression propertyAccessor) + { + var property = TryGetMemberExpression(propertyAccessor); + if (property == null) + { + throw new ArgumentException("Expected a lambda expression in the form: x => x.SomeProperty", nameof(propertyAccessor)); + } + + return property; + } + + [return: MaybeNull] + private static TMemberInfo TryGetMemberExpression(LambdaExpression lambdaExpression) + where TMemberInfo : MemberInfo + { + if (lambdaExpression.Parameters.Count != 1) + { + return null; + } + + var body = lambdaExpression.Body; + + if (body is UnaryExpression castExpression) + { + if (castExpression.NodeType != ExpressionType.Convert) + { + return null; + } + + body = castExpression.Operand; + } + + if (body is MemberExpression memberExpression) + { + if (memberExpression.Expression != lambdaExpression.Parameters[0]) + { + return null; + } + + return memberExpression.Member as TMemberInfo; + } + return null; + } + } +} +#endif diff --git a/YamlDotNet/Helpers/GenericCollectionToNonGenericAdapter.cs b/YamlDotNet/Helpers/GenericCollectionToNonGenericAdapter.cs index 3f087dc5f..b2be200cb 100644 --- a/YamlDotNet/Helpers/GenericCollectionToNonGenericAdapter.cs +++ b/YamlDotNet/Helpers/GenericCollectionToNonGenericAdapter.cs @@ -1,125 +1,125 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace YamlDotNet.Helpers -{ - /// - /// Adapts an to - /// because not all generic collections implement . - /// - internal sealed class GenericCollectionToNonGenericAdapter : IList - { - private readonly ICollection genericCollection; - - public GenericCollectionToNonGenericAdapter(ICollection genericCollection) - { - this.genericCollection = genericCollection ?? throw new ArgumentNullException(nameof(genericCollection)); - } - - public int Add(object? value) - { - var index = genericCollection.Count; - genericCollection.Add((T)value!); - return index; - } - - public void Clear() - { - genericCollection.Clear(); - } - - public bool Contains(object? value) - { - throw new NotSupportedException(); - } - - public int IndexOf(object? value) - { - throw new NotSupportedException(); - } - - public void Insert(int index, object? value) - { - throw new NotSupportedException(); - } - - public bool IsFixedSize - { - get { throw new NotSupportedException(); } - } - - public bool IsReadOnly - { - get { throw new NotSupportedException(); } - } - - public void Remove(object? value) - { - throw new NotSupportedException(); - } - - public void RemoveAt(int index) - { - throw new NotSupportedException(); - } - - public object? this[int index] - { - get - { - throw new NotSupportedException(); - } - set - { - ((IList)genericCollection)[index] = (T)value!; - } - } - - public void CopyTo(Array array, int index) - { - throw new NotSupportedException(); - } - - public int Count - { - get { throw new NotSupportedException(); } - } - - public bool IsSynchronized - { - get { throw new NotSupportedException(); } - } - - public object SyncRoot - { - get { throw new NotSupportedException(); } - } - - public IEnumerator GetEnumerator() - { - return genericCollection.GetEnumerator(); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YamlDotNet.Helpers +{ + /// + /// Adapts an to + /// because not all generic collections implement . + /// + internal sealed class GenericCollectionToNonGenericAdapter : IList + { + private readonly ICollection genericCollection; + + public GenericCollectionToNonGenericAdapter(ICollection genericCollection) + { + this.genericCollection = genericCollection ?? throw new ArgumentNullException(nameof(genericCollection)); + } + + public int Add(object? value) + { + var index = genericCollection.Count; + genericCollection.Add((T)value!); + return index; + } + + public void Clear() + { + genericCollection.Clear(); + } + + public bool Contains(object? value) + { + throw new NotSupportedException(); + } + + public int IndexOf(object? value) + { + throw new NotSupportedException(); + } + + public void Insert(int index, object? value) + { + throw new NotSupportedException(); + } + + public bool IsFixedSize + { + get { throw new NotSupportedException(); } + } + + public bool IsReadOnly + { + get { throw new NotSupportedException(); } + } + + public void Remove(object? value) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + public object? this[int index] + { + get + { + throw new NotSupportedException(); + } + set + { + ((IList)genericCollection)[index] = (T)value!; + } + } + + public void CopyTo(Array array, int index) + { + throw new NotSupportedException(); + } + + public int Count + { + get { throw new NotSupportedException(); } + } + + public bool IsSynchronized + { + get { throw new NotSupportedException(); } + } + + public object SyncRoot + { + get { throw new NotSupportedException(); } + } + + public IEnumerator GetEnumerator() + { + return genericCollection.GetEnumerator(); + } + } +} diff --git a/YamlDotNet/Helpers/GenericDictionaryToNonGenericAdapter.cs b/YamlDotNet/Helpers/GenericDictionaryToNonGenericAdapter.cs index cf12ee820..fc8eee460 100644 --- a/YamlDotNet/Helpers/GenericDictionaryToNonGenericAdapter.cs +++ b/YamlDotNet/Helpers/GenericDictionaryToNonGenericAdapter.cs @@ -1,167 +1,167 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace YamlDotNet.Helpers -{ - /// - /// Adapts an to - /// because not all generic dictionaries implement . - /// - internal sealed class GenericDictionaryToNonGenericAdapter : IDictionary - where TKey : notnull - { - private readonly IDictionary genericDictionary; - - public GenericDictionaryToNonGenericAdapter(IDictionary genericDictionary) - { - this.genericDictionary = genericDictionary ?? throw new ArgumentNullException(nameof(genericDictionary)); - } - - public void Add(object key, object? value) - { - throw new NotSupportedException(); - } - - public void Clear() - { - throw new NotSupportedException(); - } - - public bool Contains(object key) - { - throw new NotSupportedException(); - } - - public IDictionaryEnumerator GetEnumerator() - { - return new DictionaryEnumerator(genericDictionary.GetEnumerator()); - } - - public bool IsFixedSize - { - get { throw new NotSupportedException(); } - } - - public bool IsReadOnly - { - get { throw new NotSupportedException(); } - } - - public ICollection Keys - { - get { throw new NotSupportedException(); } - } - - public void Remove(object key) - { - throw new NotSupportedException(); - } - - public ICollection Values - { - get { throw new NotSupportedException(); } - } - - public object? this[object key] - { - get - { - throw new NotSupportedException(); - } - set - { - genericDictionary[(TKey)key] = (TValue)value!; - } - } - - public void CopyTo(Array array, int index) - { - throw new NotSupportedException(); - } - - public int Count - { - get { throw new NotSupportedException(); } - } - - public bool IsSynchronized - { - get { throw new NotSupportedException(); } - } - - public object SyncRoot - { - get { throw new NotSupportedException(); } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - private class DictionaryEnumerator : IDictionaryEnumerator - { - private readonly IEnumerator> enumerator; - - public DictionaryEnumerator(IEnumerator> enumerator) - { - this.enumerator = enumerator; - } - - public DictionaryEntry Entry - { - get - { - return new DictionaryEntry(Key, Value); - } - } - - public object Key - { - get { return enumerator.Current.Key!; } - } - - public object? Value - { - get { return enumerator.Current.Value; } - } - - public object Current - { - get { return Entry; } - } - - public bool MoveNext() - { - return enumerator.MoveNext(); - } - - public void Reset() - { - enumerator.Reset(); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YamlDotNet.Helpers +{ + /// + /// Adapts an to + /// because not all generic dictionaries implement . + /// + internal sealed class GenericDictionaryToNonGenericAdapter : IDictionary + where TKey : notnull + { + private readonly IDictionary genericDictionary; + + public GenericDictionaryToNonGenericAdapter(IDictionary genericDictionary) + { + this.genericDictionary = genericDictionary ?? throw new ArgumentNullException(nameof(genericDictionary)); + } + + public void Add(object key, object? value) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object key) + { + throw new NotSupportedException(); + } + + public IDictionaryEnumerator GetEnumerator() + { + return new DictionaryEnumerator(genericDictionary.GetEnumerator()); + } + + public bool IsFixedSize + { + get { throw new NotSupportedException(); } + } + + public bool IsReadOnly + { + get { throw new NotSupportedException(); } + } + + public ICollection Keys + { + get { throw new NotSupportedException(); } + } + + public void Remove(object key) + { + throw new NotSupportedException(); + } + + public ICollection Values + { + get { throw new NotSupportedException(); } + } + + public object? this[object key] + { + get + { + throw new NotSupportedException(); + } + set + { + genericDictionary[(TKey)key] = (TValue)value!; + } + } + + public void CopyTo(Array array, int index) + { + throw new NotSupportedException(); + } + + public int Count + { + get { throw new NotSupportedException(); } + } + + public bool IsSynchronized + { + get { throw new NotSupportedException(); } + } + + public object SyncRoot + { + get { throw new NotSupportedException(); } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private class DictionaryEnumerator : IDictionaryEnumerator + { + private readonly IEnumerator> enumerator; + + public DictionaryEnumerator(IEnumerator> enumerator) + { + this.enumerator = enumerator; + } + + public DictionaryEntry Entry + { + get + { + return new DictionaryEntry(Key, Value); + } + } + + public object Key + { + get { return enumerator.Current.Key!; } + } + + public object? Value + { + get { return enumerator.Current.Value; } + } + + public object Current + { + get { return Entry; } + } + + public bool MoveNext() + { + return enumerator.MoveNext(); + } + + public void Reset() + { + enumerator.Reset(); + } + } + } +} diff --git a/YamlDotNet/Helpers/IOrderedDictionary.cs b/YamlDotNet/Helpers/IOrderedDictionary.cs index fe1c9d84d..a5d44aac9 100644 --- a/YamlDotNet/Helpers/IOrderedDictionary.cs +++ b/YamlDotNet/Helpers/IOrderedDictionary.cs @@ -1,55 +1,55 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; - -namespace YamlDotNet.Helpers -{ - public interface IOrderedDictionary : IDictionary - where TKey : notnull - { - /// - /// Gets or sets the element with the specified index. - /// - /// The index of the element to get or set. - /// The element with the specified index. - KeyValuePair this[int index] - { - get; - set; - } - - /// - /// Adds an element with the provided key and value to the - /// at the given index. - /// - /// The zero-based index at which the item should be inserted. - /// The object to use as the key of the element to add. - /// The object to use as the value of the element to add. - void Insert(int index, TKey key, TValue value); - - /// - /// Removes the element at the specified index. - /// - /// The zero-based index of the element to remove. - void RemoveAt(int index); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; + +namespace YamlDotNet.Helpers +{ + public interface IOrderedDictionary : IDictionary + where TKey : notnull + { + /// + /// Gets or sets the element with the specified index. + /// + /// The index of the element to get or set. + /// The element with the specified index. + KeyValuePair this[int index] + { + get; + set; + } + + /// + /// Adds an element with the provided key and value to the + /// at the given index. + /// + /// The zero-based index at which the item should be inserted. + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + void Insert(int index, TKey key, TValue value); + + /// + /// Removes the element at the specified index. + /// + /// The zero-based index of the element to remove. + void RemoveAt(int index); + } +} diff --git a/YamlDotNet/Helpers/NumberExtensions.cs b/YamlDotNet/Helpers/NumberExtensions.cs index acc39ca25..6925ec160 100644 --- a/YamlDotNet/Helpers/NumberExtensions.cs +++ b/YamlDotNet/Helpers/NumberExtensions.cs @@ -1,31 +1,31 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Helpers -{ - internal static class NumberExtensions - { - public static bool IsPowerOfTwo(this int value) - { - return (value & (value - 1)) == 0; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Helpers +{ + internal static class NumberExtensions + { + public static bool IsPowerOfTwo(this int value) + { + return (value & (value - 1)) == 0; + } + } +} diff --git a/YamlDotNet/Helpers/OrderedDictionary.cs b/YamlDotNet/Helpers/OrderedDictionary.cs index d3d6027ba..7d5ae95f1 100644 --- a/YamlDotNet/Helpers/OrderedDictionary.cs +++ b/YamlDotNet/Helpers/OrderedDictionary.cs @@ -1,234 +1,234 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.Serialization; - -namespace YamlDotNet.Helpers -{ - [Serializable] - internal sealed class OrderedDictionary : IOrderedDictionary - where TKey : notnull - { - [NonSerialized] - private Dictionary dictionary; - private readonly List> list; - private readonly IEqualityComparer comparer; - - public TValue this[TKey key] - { - get => dictionary[key]; - set - { - if (dictionary.ContainsKey(key)) - { - var index = list.FindIndex(kvp => comparer.Equals(kvp.Key, key)); - dictionary[key] = value; - list[index] = new KeyValuePair(key, value); - } - else - { - Add(key, value); - } - } - } - - public ICollection Keys => new KeyCollection(this); - - public ICollection Values => new ValueCollection(this); - - public int Count => dictionary.Count; - - public bool IsReadOnly => false; - - public KeyValuePair this[int index] - { - get => list[index]; - set => list[index] = value; - } - - public OrderedDictionary() : this(EqualityComparer.Default) - { - } - - public OrderedDictionary(IEqualityComparer comparer) - { - list = new List>(); - dictionary = new Dictionary(comparer); - this.comparer = comparer; - } - - public void Add(TKey key, TValue value) => Add(new KeyValuePair(key, value)); - - public void Add(KeyValuePair item) - { - dictionary.Add(item.Key, item.Value); - list.Add(item); - } - - public void Clear() - { - dictionary.Clear(); - list.Clear(); - } - - public bool Contains(KeyValuePair item) => dictionary.Contains(item); - - public bool ContainsKey(TKey key) => dictionary.ContainsKey(key); - - public void CopyTo(KeyValuePair[] array, int arrayIndex) => - list.CopyTo(array, arrayIndex); - - public IEnumerator> GetEnumerator() => list.GetEnumerator(); - - public void Insert(int index, TKey key, TValue value) - { - dictionary.Add(key, value); - list.Insert(index, new KeyValuePair(key, value)); - } - - public bool Remove(TKey key) - { - if (dictionary.ContainsKey(key)) - { - var index = list.FindIndex(kvp => comparer.Equals(kvp.Key, key)); - list.RemoveAt(index); - if (!dictionary.Remove(key)) - { - throw new InvalidOperationException(); - } - return true; - } - else - { - return false; - } - } - - public bool Remove(KeyValuePair item) => Remove(item.Key); - - public void RemoveAt(int index) - { - var key = list[index].Key; - dictionary.Remove(key); - list.RemoveAt(index); - } - -#if !(NETCOREAPP3_1) -#pragma warning disable 8767 // Nullability of reference types in type of parameter ... doesn't match implicitly implemented member -#endif - - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => - dictionary.TryGetValue(key, out value); - -#if !(NETCOREAPP3_1) -#pragma warning restore 8767 -#endif - - IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator(); - - - [OnDeserialized] - internal void OnDeserializedMethod(StreamingContext context) - { - // Reconstruct the dictionary from the serialized list - dictionary = new Dictionary(); - foreach (var kvp in list) - { - dictionary[kvp.Key] = kvp.Value; - } - } - - private class KeyCollection : ICollection - { - private readonly OrderedDictionary orderedDictionary; - - public int Count => orderedDictionary.list.Count; - - public bool IsReadOnly => true; - - public void Add(TKey item) => throw new NotSupportedException(); - - public void Clear() => throw new NotSupportedException(); - - public bool Contains(TKey item) => orderedDictionary.dictionary.Keys.Contains(item); - - public KeyCollection(OrderedDictionary orderedDictionary) - { - this.orderedDictionary = orderedDictionary; - } - - public void CopyTo(TKey[] array, int arrayIndex) - { - for (var i = 0; i < orderedDictionary.list.Count; i++) - { - array[i] = orderedDictionary.list[i + arrayIndex].Key; - } - } - - public IEnumerator GetEnumerator() => - orderedDictionary.list.Select(kvp => kvp.Key).GetEnumerator(); - - public bool Remove(TKey item) => throw new NotSupportedException(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - private class ValueCollection : ICollection - { - private readonly OrderedDictionary orderedDictionary; - - public int Count => orderedDictionary.list.Count; - - public bool IsReadOnly => true; - - public void Add(TValue item) => throw new NotSupportedException(); - - public void Clear() => throw new NotSupportedException(); - - public bool Contains(TValue item) => orderedDictionary.dictionary.Values.Contains(item); - - public ValueCollection(OrderedDictionary orderedDictionary) - { - this.orderedDictionary = orderedDictionary; - } - - public void CopyTo(TValue[] array, int arrayIndex) - { - for (var i = 0; i < orderedDictionary.list.Count; i++) - { - array[i] = orderedDictionary.list[i + arrayIndex].Value; - } - } - - public IEnumerator GetEnumerator() => - orderedDictionary.list.Select(kvp => kvp.Value).GetEnumerator(); - - public bool Remove(TValue item) => throw new NotSupportedException(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.Serialization; + +namespace YamlDotNet.Helpers +{ + [Serializable] + internal sealed class OrderedDictionary : IOrderedDictionary + where TKey : notnull + { + [NonSerialized] + private Dictionary dictionary; + private readonly List> list; + private readonly IEqualityComparer comparer; + + public TValue this[TKey key] + { + get => dictionary[key]; + set + { + if (dictionary.ContainsKey(key)) + { + var index = list.FindIndex(kvp => comparer.Equals(kvp.Key, key)); + dictionary[key] = value; + list[index] = new KeyValuePair(key, value); + } + else + { + Add(key, value); + } + } + } + + public ICollection Keys => new KeyCollection(this); + + public ICollection Values => new ValueCollection(this); + + public int Count => dictionary.Count; + + public bool IsReadOnly => false; + + public KeyValuePair this[int index] + { + get => list[index]; + set => list[index] = value; + } + + public OrderedDictionary() : this(EqualityComparer.Default) + { + } + + public OrderedDictionary(IEqualityComparer comparer) + { + list = new List>(); + dictionary = new Dictionary(comparer); + this.comparer = comparer; + } + + public void Add(TKey key, TValue value) => Add(new KeyValuePair(key, value)); + + public void Add(KeyValuePair item) + { + dictionary.Add(item.Key, item.Value); + list.Add(item); + } + + public void Clear() + { + dictionary.Clear(); + list.Clear(); + } + + public bool Contains(KeyValuePair item) => dictionary.Contains(item); + + public bool ContainsKey(TKey key) => dictionary.ContainsKey(key); + + public void CopyTo(KeyValuePair[] array, int arrayIndex) => + list.CopyTo(array, arrayIndex); + + public IEnumerator> GetEnumerator() => list.GetEnumerator(); + + public void Insert(int index, TKey key, TValue value) + { + dictionary.Add(key, value); + list.Insert(index, new KeyValuePair(key, value)); + } + + public bool Remove(TKey key) + { + if (dictionary.ContainsKey(key)) + { + var index = list.FindIndex(kvp => comparer.Equals(kvp.Key, key)); + list.RemoveAt(index); + if (!dictionary.Remove(key)) + { + throw new InvalidOperationException(); + } + return true; + } + else + { + return false; + } + } + + public bool Remove(KeyValuePair item) => Remove(item.Key); + + public void RemoveAt(int index) + { + var key = list[index].Key; + dictionary.Remove(key); + list.RemoveAt(index); + } + +#if !(NETCOREAPP3_1) +#pragma warning disable 8767 // Nullability of reference types in type of parameter ... doesn't match implicitly implemented member +#endif + + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => + dictionary.TryGetValue(key, out value); + +#if !(NETCOREAPP3_1) +#pragma warning restore 8767 +#endif + + IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator(); + + + [OnDeserialized] + internal void OnDeserializedMethod(StreamingContext context) + { + // Reconstruct the dictionary from the serialized list + dictionary = new Dictionary(); + foreach (var kvp in list) + { + dictionary[kvp.Key] = kvp.Value; + } + } + + private class KeyCollection : ICollection + { + private readonly OrderedDictionary orderedDictionary; + + public int Count => orderedDictionary.list.Count; + + public bool IsReadOnly => true; + + public void Add(TKey item) => throw new NotSupportedException(); + + public void Clear() => throw new NotSupportedException(); + + public bool Contains(TKey item) => orderedDictionary.dictionary.Keys.Contains(item); + + public KeyCollection(OrderedDictionary orderedDictionary) + { + this.orderedDictionary = orderedDictionary; + } + + public void CopyTo(TKey[] array, int arrayIndex) + { + for (var i = 0; i < orderedDictionary.list.Count; i++) + { + array[i] = orderedDictionary.list[i + arrayIndex].Key; + } + } + + public IEnumerator GetEnumerator() => + orderedDictionary.list.Select(kvp => kvp.Key).GetEnumerator(); + + public bool Remove(TKey item) => throw new NotSupportedException(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + private class ValueCollection : ICollection + { + private readonly OrderedDictionary orderedDictionary; + + public int Count => orderedDictionary.list.Count; + + public bool IsReadOnly => true; + + public void Add(TValue item) => throw new NotSupportedException(); + + public void Clear() => throw new NotSupportedException(); + + public bool Contains(TValue item) => orderedDictionary.dictionary.Values.Contains(item); + + public ValueCollection(OrderedDictionary orderedDictionary) + { + this.orderedDictionary = orderedDictionary; + } + + public void CopyTo(TValue[] array, int arrayIndex) + { + for (var i = 0; i < orderedDictionary.list.Count; i++) + { + array[i] = orderedDictionary.list[i + arrayIndex].Value; + } + } + + public IEnumerator GetEnumerator() => + orderedDictionary.list.Select(kvp => kvp.Value).GetEnumerator(); + + public bool Remove(TValue item) => throw new NotSupportedException(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + } +} diff --git a/YamlDotNet/Helpers/StringBuilderPool.cs b/YamlDotNet/Helpers/StringBuilderPool.cs index d67d507c4..7b9a74475 100644 --- a/YamlDotNet/Helpers/StringBuilderPool.cs +++ b/YamlDotNet/Helpers/StringBuilderPool.cs @@ -1,77 +1,77 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Diagnostics; -using System.Text; - -namespace YamlDotNet.Helpers -{ - /// - /// Pooling of StringBuilder instances. - /// - [DebuggerStepThrough] - internal static class StringBuilderPool - { - private static readonly ConcurrentObjectPool Pool; - - static StringBuilderPool() - { - Pool = new ConcurrentObjectPool(() => new StringBuilder()); - } - - public static BuilderWrapper Rent() - { - var builder = Pool.Allocate(); - Debug.Assert(builder.Length == 0); - return new BuilderWrapper(builder, Pool); - } - - internal readonly struct BuilderWrapper : IDisposable - { - public readonly StringBuilder Builder; - private readonly ConcurrentObjectPool _pool; - - public BuilderWrapper(StringBuilder builder, ConcurrentObjectPool pool) - { - Builder = builder; - _pool = pool; - } - - public override string ToString() - { - return Builder.ToString(); - } - - public void Dispose() - { - var builder = Builder; - - // do not store builders that are too large. - if (builder.Capacity <= 1024) - { - builder.Length = 0; - _pool.Free(builder); - } - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Diagnostics; +using System.Text; + +namespace YamlDotNet.Helpers +{ + /// + /// Pooling of StringBuilder instances. + /// + [DebuggerStepThrough] + internal static class StringBuilderPool + { + private static readonly ConcurrentObjectPool Pool; + + static StringBuilderPool() + { + Pool = new ConcurrentObjectPool(() => new StringBuilder()); + } + + public static BuilderWrapper Rent() + { + var builder = Pool.Allocate(); + Debug.Assert(builder.Length == 0); + return new BuilderWrapper(builder, Pool); + } + + internal readonly struct BuilderWrapper : IDisposable + { + public readonly StringBuilder Builder; + private readonly ConcurrentObjectPool _pool; + + public BuilderWrapper(StringBuilder builder, ConcurrentObjectPool pool) + { + Builder = builder; + _pool = pool; + } + + public override string ToString() + { + return Builder.ToString(); + } + + public void Dispose() + { + var builder = Builder; + + // do not store builders that are too large. + if (builder.Capacity <= 1024) + { + builder.Length = 0; + _pool.Free(builder); + } + } + } + } +} diff --git a/YamlDotNet/Helpers/ThrowHelper.cs b/YamlDotNet/Helpers/ThrowHelper.cs index f347b1441..babccb725 100644 --- a/YamlDotNet/Helpers/ThrowHelper.cs +++ b/YamlDotNet/Helpers/ThrowHelper.cs @@ -1,35 +1,35 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Runtime.CompilerServices; - -namespace YamlDotNet.Helpers -{ - internal static class ThrowHelper - { - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowArgumentOutOfRangeException(string paramName, string message) - { - throw new ArgumentOutOfRangeException(paramName, message); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Runtime.CompilerServices; + +namespace YamlDotNet.Helpers +{ + internal static class ThrowHelper + { + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowArgumentOutOfRangeException(string paramName, string message) + { + throw new ArgumentOutOfRangeException(paramName, message); + } + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5+net45+net47/include/DeconstructionExtensions.cs b/YamlDotNet/Portability/net35+unitysubset3.5+net45+net47/include/DeconstructionExtensions.cs index 571fb38e0..734c41608 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5+net45+net47/include/DeconstructionExtensions.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5+net45+net47/include/DeconstructionExtensions.cs @@ -1,32 +1,32 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace System.Collections.Generic -{ - internal static class DeconstructionExtensions - { - public static void Deconstruct(this KeyValuePair pair, out TKey key, out TValue value) - { - key = pair.Key; - value = pair.Value; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace System.Collections.Generic +{ + internal static class DeconstructionExtensions + { + public static void Deconstruct(this KeyValuePair pair, out TKey key, out TValue value) + { + key = pair.Key; + value = pair.Value; + } + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5/exclude/ReadOnlyCollectionExtensions.cs b/YamlDotNet/Portability/net35+unitysubset3.5/exclude/ReadOnlyCollectionExtensions.cs index f93fd6e48..3c00dca61 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5/exclude/ReadOnlyCollectionExtensions.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5/exclude/ReadOnlyCollectionExtensions.cs @@ -1,38 +1,38 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; - -namespace YamlDotNet.Helpers -{ - internal static class ReadOnlyCollectionExtensions - { - public static IReadOnlyList AsReadonlyList(this List list) - { - return list; - } - - public static IReadOnlyDictionary AsReadonlyDictionary(this Dictionary dictionary) where TKey : notnull - { - return dictionary; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; + +namespace YamlDotNet.Helpers +{ + internal static class ReadOnlyCollectionExtensions + { + public static IReadOnlyList AsReadonlyList(this List list) + { + return list; + } + + public static IReadOnlyDictionary AsReadonlyDictionary(this Dictionary dictionary) where TKey : notnull + { + return dictionary; + } + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5/include/ConcurrentDictionary.cs b/YamlDotNet/Portability/net35+unitysubset3.5/include/ConcurrentDictionary.cs index b36be4180..81b51a179 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5/include/ConcurrentDictionary.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5/include/ConcurrentDictionary.cs @@ -1,67 +1,67 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace System.Collections.Concurrent -{ - internal sealed class ConcurrentDictionary - { - private readonly Dictionary entries = new Dictionary(); - - public delegate TValue ValueFactory(TKey key); - - public TValue GetOrAdd(TKey key, ValueFactory valueFactory) - { - lock (entries) - { - if (!entries.TryGetValue(key, out var value)) - { - value = valueFactory(key); - entries.Add(key, value); - } - return value; - } - } - - public bool TryAdd(TKey key, TValue value) - { - lock (entries) - { - if (!entries.ContainsKey(key)) - { - entries.Add(key, value); - return true; - } - return false; - } - } - - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) - { - lock (entries) - { - return entries.TryGetValue(key, out value); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace System.Collections.Concurrent +{ + internal sealed class ConcurrentDictionary + { + private readonly Dictionary entries = new Dictionary(); + + public delegate TValue ValueFactory(TKey key); + + public TValue GetOrAdd(TKey key, ValueFactory valueFactory) + { + lock (entries) + { + if (!entries.TryGetValue(key, out var value)) + { + value = valueFactory(key); + entries.Add(key, value); + } + return value; + } + } + + public bool TryAdd(TKey key, TValue value) + { + lock (entries) + { + if (!entries.ContainsKey(key)) + { + entries.Add(key, value); + return true; + } + return false; + } + } + + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + { + lock (entries) + { + return entries.TryGetValue(key, out value); + } + } + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyCollection.cs b/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyCollection.cs index 5219186d2..d1c1475c0 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyCollection.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyCollection.cs @@ -1,28 +1,28 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace System.Collections.Generic -{ - public interface IReadOnlyCollection : IEnumerable, IEnumerable - { - int Count { get; } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace System.Collections.Generic +{ + public interface IReadOnlyCollection : IEnumerable, IEnumerable + { + int Count { get; } + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyDictionary.cs b/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyDictionary.cs index a7688c9a9..31ea4594b 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyDictionary.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyDictionary.cs @@ -1,34 +1,34 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Diagnostics.CodeAnalysis; - -namespace System.Collections.Generic -{ - public interface IReadOnlyDictionary : IEnumerable>, IEnumerable, IReadOnlyCollection> where TKey : notnull - { - TValue this[TKey key] { get; } - IEnumerable Keys { get; } - IEnumerable Values { get; } - bool ContainsKey(TKey key); - bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Diagnostics.CodeAnalysis; + +namespace System.Collections.Generic +{ + public interface IReadOnlyDictionary : IEnumerable>, IEnumerable, IReadOnlyCollection> where TKey : notnull + { + TValue this[TKey key] { get; } + IEnumerable Keys { get; } + IEnumerable Values { get; } + bool ContainsKey(TKey key); + bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value); + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyList.cs b/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyList.cs index 65289a736..221908b39 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyList.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5/include/IReadOnlyList.cs @@ -1,28 +1,28 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace System.Collections.Generic -{ - public interface IReadOnlyList : IEnumerable, IEnumerable, IReadOnlyCollection - { - T this[int index] { get; } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace System.Collections.Generic +{ + public interface IReadOnlyList : IEnumerable, IEnumerable, IReadOnlyCollection + { + T this[int index] { get; } + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5/include/Lazy.cs b/YamlDotNet/Portability/net35+unitysubset3.5/include/Lazy.cs index 990a8c7ca..40272c8f2 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5/include/Lazy.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5/include/Lazy.cs @@ -1,94 +1,94 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace System -{ - internal sealed class Lazy - { - private enum ValueState - { - NotCreated, - Creating, - Created, - } - - private T value; - private Func? valueFactory; - private ValueState valueState; - private readonly bool isThreadSafe; - - public Lazy(T value) - { - this.value = value; - valueState = ValueState.Created; - } - - public Lazy(Func valueFactory) - { - this.valueFactory = valueFactory; - this.isThreadSafe = false; - valueState = ValueState.NotCreated; - } - - public Lazy(Func valueFactory, bool isThreadSafe) - { - this.valueFactory = valueFactory; - this.isThreadSafe = isThreadSafe; - valueState = ValueState.NotCreated; - value = default!; - } - - public T Value - { - get - { - if (isThreadSafe) - { - lock (this) - { - return ComputeValue(); - } - } - else - { - return ComputeValue(); - } - } - } - - private T ComputeValue() - { - switch (valueState) - { - case Lazy.ValueState.NotCreated: - valueState = ValueState.Creating; - value = valueFactory(); - valueState = ValueState.Created; - valueFactory = null; - break; - - case Lazy.ValueState.Creating: - throw new InvalidOperationException("ValueFactory attempted to access the Value property of this instance."); - } - return value; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace System +{ + internal sealed class Lazy + { + private enum ValueState + { + NotCreated, + Creating, + Created, + } + + private T value; + private Func? valueFactory; + private ValueState valueState; + private readonly bool isThreadSafe; + + public Lazy(T value) + { + this.value = value; + valueState = ValueState.Created; + } + + public Lazy(Func valueFactory) + { + this.valueFactory = valueFactory; + this.isThreadSafe = false; + valueState = ValueState.NotCreated; + } + + public Lazy(Func valueFactory, bool isThreadSafe) + { + this.valueFactory = valueFactory; + this.isThreadSafe = isThreadSafe; + valueState = ValueState.NotCreated; + value = default!; + } + + public T Value + { + get + { + if (isThreadSafe) + { + lock (this) + { + return ComputeValue(); + } + } + else + { + return ComputeValue(); + } + } + } + + private T ComputeValue() + { + switch (valueState) + { + case Lazy.ValueState.NotCreated: + valueState = ValueState.Creating; + value = valueFactory(); + valueState = ValueState.Created; + valueFactory = null; + break; + + case Lazy.ValueState.Creating: + throw new InvalidOperationException("ValueFactory attempted to access the Value property of this instance."); + } + return value; + } + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5/include/Linq.cs b/YamlDotNet/Portability/net35+unitysubset3.5/include/Linq.cs index e129cc072..482f9ae3a 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5/include/Linq.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5/include/Linq.cs @@ -1,38 +1,38 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; - -namespace System.Linq -{ - internal static partial class Enumerable2 - { - public static IEnumerable Zip(this IEnumerable first, IEnumerable second, Func resultSelector) - { - using var firstEnumerator = first.GetEnumerator(); - using var secondEnumerator = second.GetEnumerator(); - while (firstEnumerator.MoveNext() && secondEnumerator.MoveNext()) - { - yield return resultSelector(firstEnumerator.Current, secondEnumerator.Current); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; + +namespace System.Linq +{ + internal static partial class Enumerable2 + { + public static IEnumerable Zip(this IEnumerable first, IEnumerable second, Func resultSelector) + { + using var firstEnumerator = first.GetEnumerator(); + using var secondEnumerator = second.GetEnumerator(); + while (firstEnumerator.MoveNext() && secondEnumerator.MoveNext()) + { + yield return resultSelector(firstEnumerator.Current, secondEnumerator.Current); + } + } + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5/include/ReadOnlyCollectionExtensions.cs b/YamlDotNet/Portability/net35+unitysubset3.5/include/ReadOnlyCollectionExtensions.cs index 3869fee5f..787dd559f 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5/include/ReadOnlyCollectionExtensions.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5/include/ReadOnlyCollectionExtensions.cs @@ -1,83 +1,83 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace YamlDotNet.Helpers -{ - internal static class ReadOnlyCollectionExtensions - { - private sealed class ReadOnlyListAdapter : IReadOnlyList - { - private readonly List list; - - public ReadOnlyListAdapter(List list) - { - this.list = list ?? throw new ArgumentNullException(nameof(list)); - } - - public T this[int index] => list[index]; - public int Count => list.Count; - public IEnumerator GetEnumerator() => list.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator(); - } - - public static IReadOnlyList AsReadonlyList(this List list) - { - return new ReadOnlyListAdapter(list); - } - - private sealed class ReadOnlyDictionaryAdapter : IReadOnlyDictionary where TKey : notnull - { - private readonly Dictionary dictionary; - - public ReadOnlyDictionaryAdapter(Dictionary dictionary) - { - this.dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); - } - - public TValue this[TKey key] => dictionary[key]; - - public IEnumerable Keys => dictionary.Keys; - - public IEnumerable Values => dictionary.Values; - - public int Count => dictionary.Count; - - public bool ContainsKey(TKey key) => dictionary.ContainsKey(key); - - public IEnumerator> GetEnumerator() => dictionary.GetEnumerator(); - - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => dictionary.TryGetValue(key, out value); - - IEnumerator IEnumerable.GetEnumerator() => dictionary.GetEnumerator(); - } - - public static IReadOnlyDictionary AsReadonlyDictionary(this Dictionary dictionary) where TKey : notnull - { - return new ReadOnlyDictionaryAdapter(dictionary); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace YamlDotNet.Helpers +{ + internal static class ReadOnlyCollectionExtensions + { + private sealed class ReadOnlyListAdapter : IReadOnlyList + { + private readonly List list; + + public ReadOnlyListAdapter(List list) + { + this.list = list ?? throw new ArgumentNullException(nameof(list)); + } + + public T this[int index] => list[index]; + public int Count => list.Count; + public IEnumerator GetEnumerator() => list.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator(); + } + + public static IReadOnlyList AsReadonlyList(this List list) + { + return new ReadOnlyListAdapter(list); + } + + private sealed class ReadOnlyDictionaryAdapter : IReadOnlyDictionary where TKey : notnull + { + private readonly Dictionary dictionary; + + public ReadOnlyDictionaryAdapter(Dictionary dictionary) + { + this.dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); + } + + public TValue this[TKey key] => dictionary[key]; + + public IEnumerable Keys => dictionary.Keys; + + public IEnumerable Values => dictionary.Values; + + public int Count => dictionary.Count; + + public bool ContainsKey(TKey key) => dictionary.ContainsKey(key); + + public IEnumerator> GetEnumerator() => dictionary.GetEnumerator(); + + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => dictionary.TryGetValue(key, out value); + + IEnumerator IEnumerable.GetEnumerator() => dictionary.GetEnumerator(); + } + + public static IReadOnlyDictionary AsReadonlyDictionary(this Dictionary dictionary) where TKey : notnull + { + return new ReadOnlyDictionaryAdapter(dictionary); + } + } +} diff --git a/YamlDotNet/Portability/net35+unitysubset3.5/include/ValueTuple.cs b/YamlDotNet/Portability/net35+unitysubset3.5/include/ValueTuple.cs index b64855013..7a34c15b6 100644 --- a/YamlDotNet/Portability/net35+unitysubset3.5/include/ValueTuple.cs +++ b/YamlDotNet/Portability/net35+unitysubset3.5/include/ValueTuple.cs @@ -1,50 +1,50 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; - -namespace System.Runtime.CompilerServices -{ - internal sealed class TupleElementNamesAttribute : Attribute - { - public TupleElementNamesAttribute(string?[] transformNames) - { - TransformNames = transformNames; - } - - public IList TransformNames { get; } - } -} - -namespace System -{ - public struct ValueTuple - { - public T1 Item1; - public T2 Item2; - - public ValueTuple(T1 item1, T2 item2) - { - this.Item1 = item1; - this.Item2 = item2; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; + +namespace System.Runtime.CompilerServices +{ + internal sealed class TupleElementNamesAttribute : Attribute + { + public TupleElementNamesAttribute(string?[] transformNames) + { + TransformNames = transformNames; + } + + public IList TransformNames { get; } + } +} + +namespace System +{ + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + + public ValueTuple(T1 item1, T2 item2) + { + this.Item1 = item1; + this.Item2 = item2; + } + } +} diff --git a/YamlDotNet/Portability/net45+net47/include/LazyBuilder.cs b/YamlDotNet/Portability/net45+net47/include/LazyBuilder.cs index f6e00f2e9..6234d00b1 100644 --- a/YamlDotNet/Portability/net45+net47/include/LazyBuilder.cs +++ b/YamlDotNet/Portability/net45+net47/include/LazyBuilder.cs @@ -1,35 +1,35 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Helpers -{ - internal static class Lazy - { - public static Lazy FromValue(T value) - { - var lazy = new Lazy(() => value, isThreadSafe: false); - var _ = lazy.Value; // Force evaluation - return lazy; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Helpers +{ + internal static class Lazy + { + public static Lazy FromValue(T value) + { + var lazy = new Lazy(() => value, isThreadSafe: false); + var _ = lazy.Value; // Force evaluation + return lazy; + } + } +} diff --git a/YamlDotNet/Portability/netstandard2.0+netstandard2.1/exclude/CultureInfoAdapter.cs b/YamlDotNet/Portability/netstandard2.0+netstandard2.1/exclude/CultureInfoAdapter.cs index afa040468..15018f56b 100644 --- a/YamlDotNet/Portability/netstandard2.0+netstandard2.1/exclude/CultureInfoAdapter.cs +++ b/YamlDotNet/Portability/netstandard2.0+netstandard2.1/exclude/CultureInfoAdapter.cs @@ -1,42 +1,42 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Globalization; - -namespace YamlDotNet -{ - internal sealed class CultureInfoAdapter : CultureInfo - { - private readonly IFormatProvider provider; - - public CultureInfoAdapter(CultureInfo baseCulture, IFormatProvider provider) - : base(baseCulture.LCID) - { - this.provider = provider; - } - - public override object? GetFormat(Type? formatType) - { - return provider.GetFormat(formatType); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Globalization; + +namespace YamlDotNet +{ + internal sealed class CultureInfoAdapter : CultureInfo + { + private readonly IFormatProvider provider; + + public CultureInfoAdapter(CultureInfo baseCulture, IFormatProvider provider) + : base(baseCulture.LCID) + { + this.provider = provider; + } + + public override object? GetFormat(Type? formatType) + { + return provider.GetFormat(formatType); + } + } +} diff --git a/YamlDotNet/Portability/netstandard2.0+netstandard2.1/exclude/ReflectionExtensions.cs b/YamlDotNet/Portability/netstandard2.0+netstandard2.1/exclude/ReflectionExtensions.cs index 91c88b9d0..37a087a85 100644 --- a/YamlDotNet/Portability/netstandard2.0+netstandard2.1/exclude/ReflectionExtensions.cs +++ b/YamlDotNet/Portability/netstandard2.0+netstandard2.1/exclude/ReflectionExtensions.cs @@ -1,166 +1,166 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace YamlDotNet -{ - internal static class ReflectionExtensions - { - public static Type? BaseType(this Type type) - { - return type.BaseType; - } - - public static bool IsValueType(this Type type) - { - return type.IsValueType; - } - - public static bool IsGenericType(this Type type) - { - return type.IsGenericType; - } - - public static bool IsGenericTypeDefinition(this Type type) - { - return type.IsGenericTypeDefinition; - } - - public static bool IsInterface(this Type type) - { - return type.IsInterface; - } - - public static bool IsEnum(this Type type) - { - return type.IsEnum; - } - - public static bool IsDbNull(this object value) - { - return value is DBNull; - } - - /// - /// Determines whether the specified type has a default constructor. - /// - /// The type. - /// - /// true if the type has a default constructor; otherwise, false. - /// - public static bool HasDefaultConstructor(this Type type) - { - return type.IsValueType || type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null) != null; - } - - public static TypeCode GetTypeCode(this Type type) - { - return Type.GetTypeCode(type); - } - - public static PropertyInfo? GetPublicProperty(this Type type, string name) - { - return type.GetProperty(name); - } - - public static FieldInfo? GetPublicStaticField(this Type type, string name) - { - return type.GetField(name, BindingFlags.Static | BindingFlags.Public); - } - - public static IEnumerable GetProperties(this Type type, bool includeNonPublic) - { - var bindingFlags = BindingFlags.Instance | BindingFlags.Public; - - if (includeNonPublic) - { - bindingFlags |= BindingFlags.NonPublic; - } - - return type.IsInterface - ? (new Type[] { type }) - .Concat(type.GetInterfaces()) - .SelectMany(i => i.GetProperties(bindingFlags)) - : type.GetProperties(bindingFlags); - } - - public static IEnumerable GetPublicProperties(this Type type) => GetProperties(type, false); - - public static IEnumerable GetPublicFields(this Type type) - { - return type.GetFields(BindingFlags.Instance | BindingFlags.Public); - } - - public static IEnumerable GetPublicStaticMethods(this Type type) - { - return type.GetMethods(BindingFlags.Static | BindingFlags.Public); - } - - public static MethodInfo GetPrivateStaticMethod(this Type type, string name) - { - return type.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic) - ?? throw new MissingMethodException($"Expected to find a method named '{name}' in '{type.FullName}'."); - } - - public static MethodInfo? GetPublicStaticMethod(this Type type, string name, params Type[] parameterTypes) - { - return type.GetMethod(name, BindingFlags.Public | BindingFlags.Static, null, parameterTypes, null); - } - - public static MethodInfo? GetPublicInstanceMethod(this Type type, string name) - { - return type.GetMethod(name, BindingFlags.Public | BindingFlags.Instance); - } - - private static readonly FieldInfo? RemoteStackTraceField = typeof(Exception) - .GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic); - - public static Exception Unwrap(this TargetInvocationException ex) - { - var result = ex.InnerException; - if (result == null) - { - return ex; - } - - if (RemoteStackTraceField != null) - { - RemoteStackTraceField.SetValue(result, result.StackTrace + "\r\n"); - } - return result; - } - - public static bool IsInstanceOf(this Type type, object o) - { - return type.IsInstanceOfType(o); - } - - public static Attribute[] GetAllCustomAttributes(this PropertyInfo property) - { - // Don't use IMemberInfo.GetCustomAttributes, it ignores the inherit parameter - return Attribute.GetCustomAttributes(property, typeof(TAttribute)); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace YamlDotNet +{ + internal static class ReflectionExtensions + { + public static Type? BaseType(this Type type) + { + return type.BaseType; + } + + public static bool IsValueType(this Type type) + { + return type.IsValueType; + } + + public static bool IsGenericType(this Type type) + { + return type.IsGenericType; + } + + public static bool IsGenericTypeDefinition(this Type type) + { + return type.IsGenericTypeDefinition; + } + + public static bool IsInterface(this Type type) + { + return type.IsInterface; + } + + public static bool IsEnum(this Type type) + { + return type.IsEnum; + } + + public static bool IsDbNull(this object value) + { + return value is DBNull; + } + + /// + /// Determines whether the specified type has a default constructor. + /// + /// The type. + /// + /// true if the type has a default constructor; otherwise, false. + /// + public static bool HasDefaultConstructor(this Type type) + { + return type.IsValueType || type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null) != null; + } + + public static TypeCode GetTypeCode(this Type type) + { + return Type.GetTypeCode(type); + } + + public static PropertyInfo? GetPublicProperty(this Type type, string name) + { + return type.GetProperty(name); + } + + public static FieldInfo? GetPublicStaticField(this Type type, string name) + { + return type.GetField(name, BindingFlags.Static | BindingFlags.Public); + } + + public static IEnumerable GetProperties(this Type type, bool includeNonPublic) + { + var bindingFlags = BindingFlags.Instance | BindingFlags.Public; + + if (includeNonPublic) + { + bindingFlags |= BindingFlags.NonPublic; + } + + return type.IsInterface + ? (new Type[] { type }) + .Concat(type.GetInterfaces()) + .SelectMany(i => i.GetProperties(bindingFlags)) + : type.GetProperties(bindingFlags); + } + + public static IEnumerable GetPublicProperties(this Type type) => GetProperties(type, false); + + public static IEnumerable GetPublicFields(this Type type) + { + return type.GetFields(BindingFlags.Instance | BindingFlags.Public); + } + + public static IEnumerable GetPublicStaticMethods(this Type type) + { + return type.GetMethods(BindingFlags.Static | BindingFlags.Public); + } + + public static MethodInfo GetPrivateStaticMethod(this Type type, string name) + { + return type.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new MissingMethodException($"Expected to find a method named '{name}' in '{type.FullName}'."); + } + + public static MethodInfo? GetPublicStaticMethod(this Type type, string name, params Type[] parameterTypes) + { + return type.GetMethod(name, BindingFlags.Public | BindingFlags.Static, null, parameterTypes, null); + } + + public static MethodInfo? GetPublicInstanceMethod(this Type type, string name) + { + return type.GetMethod(name, BindingFlags.Public | BindingFlags.Instance); + } + + private static readonly FieldInfo? RemoteStackTraceField = typeof(Exception) + .GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic); + + public static Exception Unwrap(this TargetInvocationException ex) + { + var result = ex.InnerException; + if (result == null) + { + return ex; + } + + if (RemoteStackTraceField != null) + { + RemoteStackTraceField.SetValue(result, result.StackTrace + "\r\n"); + } + return result; + } + + public static bool IsInstanceOf(this Type type, object o) + { + return type.IsInstanceOfType(o); + } + + public static Attribute[] GetAllCustomAttributes(this PropertyInfo property) + { + // Don't use IMemberInfo.GetCustomAttributes, it ignores the inherit parameter + return Attribute.GetCustomAttributes(property, typeof(TAttribute)); + } + } +} diff --git a/YamlDotNet/Portability/netstandard2.0+netstandard2.1/include/CultureInfoAdapter.cs b/YamlDotNet/Portability/netstandard2.0+netstandard2.1/include/CultureInfoAdapter.cs index 8a10d8142..f315d766e 100644 --- a/YamlDotNet/Portability/netstandard2.0+netstandard2.1/include/CultureInfoAdapter.cs +++ b/YamlDotNet/Portability/netstandard2.0+netstandard2.1/include/CultureInfoAdapter.cs @@ -1,42 +1,42 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Globalization; - -namespace YamlDotNet -{ - internal sealed class CultureInfoAdapter : CultureInfo - { - private readonly IFormatProvider provider; - - public CultureInfoAdapter(CultureInfo baseCulture, IFormatProvider provider) - : base(baseCulture.Name) - { - this.provider = provider; - } - - public override object GetFormat(Type formatType) - { - return provider.GetFormat(formatType); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Globalization; + +namespace YamlDotNet +{ + internal sealed class CultureInfoAdapter : CultureInfo + { + private readonly IFormatProvider provider; + + public CultureInfoAdapter(CultureInfo baseCulture, IFormatProvider provider) + : base(baseCulture.Name) + { + this.provider = provider; + } + + public override object GetFormat(Type formatType) + { + return provider.GetFormat(formatType); + } + } +} diff --git a/YamlDotNet/Portability/netstandard2.0+netstandard2.1/include/ReflectionExtensions.cs b/YamlDotNet/Portability/netstandard2.0+netstandard2.1/include/ReflectionExtensions.cs index 40c4ed1c0..2c6ea7c4b 100644 --- a/YamlDotNet/Portability/netstandard2.0+netstandard2.1/include/ReflectionExtensions.cs +++ b/YamlDotNet/Portability/netstandard2.0+netstandard2.1/include/ReflectionExtensions.cs @@ -1,289 +1,289 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace YamlDotNet -{ - internal static class ReflectionExtensions - { - public static Type? BaseType(this Type type) - { - return type.GetTypeInfo().BaseType; - } - - public static bool IsValueType(this Type type) - { - return type.GetTypeInfo().IsValueType; - } - - public static bool IsGenericType(this Type type) - { - return type.GetTypeInfo().IsGenericType; - } - - public static bool IsGenericTypeDefinition(this Type type) - { - return type.GetTypeInfo().IsGenericTypeDefinition; - } - - public static bool IsInterface(this Type type) - { - return type.GetTypeInfo().IsInterface; - } - - public static bool IsEnum(this Type type) - { - return type.GetTypeInfo().IsEnum; - } - - /// - /// Determines whether the specified type has a default constructor. - /// - /// The type. - /// - /// true if the type has a default constructor; otherwise, false. - /// - public static bool HasDefaultConstructor(this Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsValueType || typeInfo.DeclaredConstructors - .Any(c => c.IsPublic && !c.IsStatic && c.GetParameters().Length == 0); - } - - public static bool IsAssignableFrom(this Type type, Type source) - { - return type.IsAssignableFrom(source.GetTypeInfo()); - } - - public static bool IsAssignableFrom(this Type type, TypeInfo source) - { - return type.GetTypeInfo().IsAssignableFrom(source); - } - - public static TypeCode GetTypeCode(this Type type) - { - var isEnum = type.IsEnum(); - if (isEnum) - { - type = Enum.GetUnderlyingType(type); - } - - if (type == typeof(bool)) - { - return TypeCode.Boolean; - } - else if (type == typeof(char)) - { - return TypeCode.Char; - } - else if (type == typeof(sbyte)) - { - return TypeCode.SByte; - } - else if (type == typeof(byte)) - { - return TypeCode.Byte; - } - else if (type == typeof(short)) - { - return TypeCode.Int16; - } - else if (type == typeof(ushort)) - { - return TypeCode.UInt16; - } - else if (type == typeof(int)) - { - return TypeCode.Int32; - } - else if (type == typeof(uint)) - { - return TypeCode.UInt32; - } - else if (type == typeof(long)) - { - return TypeCode.Int64; - } - else if (type == typeof(ulong)) - { - return TypeCode.UInt64; - } - else if (type == typeof(float)) - { - return TypeCode.Single; - } - else if (type == typeof(double)) - { - return TypeCode.Double; - } - else if (type == typeof(decimal)) - { - return TypeCode.Decimal; - } - else if (type == typeof(DateTime)) - { - return TypeCode.DateTime; - } - else if (type == typeof(string)) - { - return TypeCode.String; - } - else - { - return TypeCode.Object; - } - } - - public static bool IsDbNull(this object value) - { - return value?.GetType()?.FullName == "System.DBNull"; - } - - public static Type[] GetGenericArguments(this Type type) - { - return type.GetTypeInfo().GenericTypeArguments; - } - - public static PropertyInfo? GetPublicProperty(this Type type, string name) - { - return type.GetRuntimeProperty(name); - } - - public static FieldInfo? GetPublicStaticField(this Type type, string name) - { - return type.GetRuntimeField(name); - } - - public static IEnumerable GetConstructors(this Type type) - { - return type.GetTypeInfo().DeclaredConstructors - .Where(c => c.IsPublic && !c.IsStatic); - } - - private static readonly Func IsInstance = (PropertyInfo property) => !(property.GetMethod ?? property.SetMethod).IsStatic; - private static readonly Func IsInstancePublic = (PropertyInfo property) => IsInstance(property) && (property.GetMethod ?? property.SetMethod).IsPublic; - - public static IEnumerable GetProperties(this Type type, bool includeNonPublic) - { - var predicate = includeNonPublic ? IsInstance : IsInstancePublic; - - return type.IsInterface() - ? (new Type[] { type }) - .Concat(type.GetInterfaces()) - .SelectMany(i => i.GetRuntimeProperties().Where(predicate)) - : type.GetRuntimeProperties().Where(predicate); - } - - public static IEnumerable GetPublicProperties(this Type type) => GetProperties(type, false); - - public static IEnumerable GetPublicFields(this Type type) - { - return type.GetRuntimeFields().Where(f => !f.IsStatic && f.IsPublic); - } - - public static IEnumerable GetPublicStaticMethods(this Type type) - { - return type.GetRuntimeMethods() - .Where(m => m.IsPublic && m.IsStatic); - } - - public static MethodInfo GetPrivateStaticMethod(this Type type, string name) - { - return type.GetRuntimeMethods() - .FirstOrDefault(m => !m.IsPublic && m.IsStatic && m.Name.Equals(name)) - ?? throw new MissingMethodException($"Expected to find a method named '{name}' in '{type.FullName}'."); - } - - public static MethodInfo? GetPublicStaticMethod(this Type type, string name, params Type[] parameterTypes) - { - return type.GetRuntimeMethods() - .FirstOrDefault(m => - { - if (m.IsPublic && m.IsStatic && m.Name.Equals(name)) - { - var parameters = m.GetParameters(); - return parameters.Length == parameterTypes.Length - && parameters.Zip(parameterTypes, (pi, pt) => pi.ParameterType == pt).All(r => r); - } - return false; - }); - } - - public static MethodInfo? GetPublicInstanceMethod(this Type type, string name) - { - return type.GetRuntimeMethods() - .FirstOrDefault(m => m.IsPublic && !m.IsStatic && m.Name.Equals(name)); - } - - public static MethodInfo? GetGetMethod(this PropertyInfo property, bool nonPublic) - { - var getter = property.GetMethod; - if (!nonPublic && !getter.IsPublic) - { - getter = null; - } - return getter; - } - - public static MethodInfo GetSetMethod(this PropertyInfo property) - { - return property.SetMethod; - } - - public static IEnumerable GetInterfaces(this Type type) - { - return type.GetTypeInfo().ImplementedInterfaces; - } - - public static Exception Unwrap(this TargetInvocationException ex) - { - return ex.InnerException; - } - - public static bool IsInstanceOf(this Type type, object o) - { - return o.GetType() == type || o.GetType().GetTypeInfo().IsSubclassOf(type); - } - - public static Attribute[] GetAllCustomAttributes(this PropertyInfo member) - { - // IMemberInfo.GetCustomAttributes ignores it's "inherit" parameter for properties, - // and the suggested replacement (Attribute.GetCustomAttributes) is not available - // on netstandard1.3 - var result = new List(); - var type = member.DeclaringType; - - while (type != null) - { - type.GetPublicProperty(member.Name); - result.AddRange(member.GetCustomAttributes(typeof(TAttribute))); - - type = type.BaseType(); - } - - return result.ToArray(); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace YamlDotNet +{ + internal static class ReflectionExtensions + { + public static Type? BaseType(this Type type) + { + return type.GetTypeInfo().BaseType; + } + + public static bool IsValueType(this Type type) + { + return type.GetTypeInfo().IsValueType; + } + + public static bool IsGenericType(this Type type) + { + return type.GetTypeInfo().IsGenericType; + } + + public static bool IsGenericTypeDefinition(this Type type) + { + return type.GetTypeInfo().IsGenericTypeDefinition; + } + + public static bool IsInterface(this Type type) + { + return type.GetTypeInfo().IsInterface; + } + + public static bool IsEnum(this Type type) + { + return type.GetTypeInfo().IsEnum; + } + + /// + /// Determines whether the specified type has a default constructor. + /// + /// The type. + /// + /// true if the type has a default constructor; otherwise, false. + /// + public static bool HasDefaultConstructor(this Type type) + { + var typeInfo = type.GetTypeInfo(); + return typeInfo.IsValueType || typeInfo.DeclaredConstructors + .Any(c => c.IsPublic && !c.IsStatic && c.GetParameters().Length == 0); + } + + public static bool IsAssignableFrom(this Type type, Type source) + { + return type.IsAssignableFrom(source.GetTypeInfo()); + } + + public static bool IsAssignableFrom(this Type type, TypeInfo source) + { + return type.GetTypeInfo().IsAssignableFrom(source); + } + + public static TypeCode GetTypeCode(this Type type) + { + var isEnum = type.IsEnum(); + if (isEnum) + { + type = Enum.GetUnderlyingType(type); + } + + if (type == typeof(bool)) + { + return TypeCode.Boolean; + } + else if (type == typeof(char)) + { + return TypeCode.Char; + } + else if (type == typeof(sbyte)) + { + return TypeCode.SByte; + } + else if (type == typeof(byte)) + { + return TypeCode.Byte; + } + else if (type == typeof(short)) + { + return TypeCode.Int16; + } + else if (type == typeof(ushort)) + { + return TypeCode.UInt16; + } + else if (type == typeof(int)) + { + return TypeCode.Int32; + } + else if (type == typeof(uint)) + { + return TypeCode.UInt32; + } + else if (type == typeof(long)) + { + return TypeCode.Int64; + } + else if (type == typeof(ulong)) + { + return TypeCode.UInt64; + } + else if (type == typeof(float)) + { + return TypeCode.Single; + } + else if (type == typeof(double)) + { + return TypeCode.Double; + } + else if (type == typeof(decimal)) + { + return TypeCode.Decimal; + } + else if (type == typeof(DateTime)) + { + return TypeCode.DateTime; + } + else if (type == typeof(string)) + { + return TypeCode.String; + } + else + { + return TypeCode.Object; + } + } + + public static bool IsDbNull(this object value) + { + return value?.GetType()?.FullName == "System.DBNull"; + } + + public static Type[] GetGenericArguments(this Type type) + { + return type.GetTypeInfo().GenericTypeArguments; + } + + public static PropertyInfo? GetPublicProperty(this Type type, string name) + { + return type.GetRuntimeProperty(name); + } + + public static FieldInfo? GetPublicStaticField(this Type type, string name) + { + return type.GetRuntimeField(name); + } + + public static IEnumerable GetConstructors(this Type type) + { + return type.GetTypeInfo().DeclaredConstructors + .Where(c => c.IsPublic && !c.IsStatic); + } + + private static readonly Func IsInstance = (PropertyInfo property) => !(property.GetMethod ?? property.SetMethod).IsStatic; + private static readonly Func IsInstancePublic = (PropertyInfo property) => IsInstance(property) && (property.GetMethod ?? property.SetMethod).IsPublic; + + public static IEnumerable GetProperties(this Type type, bool includeNonPublic) + { + var predicate = includeNonPublic ? IsInstance : IsInstancePublic; + + return type.IsInterface() + ? (new Type[] { type }) + .Concat(type.GetInterfaces()) + .SelectMany(i => i.GetRuntimeProperties().Where(predicate)) + : type.GetRuntimeProperties().Where(predicate); + } + + public static IEnumerable GetPublicProperties(this Type type) => GetProperties(type, false); + + public static IEnumerable GetPublicFields(this Type type) + { + return type.GetRuntimeFields().Where(f => !f.IsStatic && f.IsPublic); + } + + public static IEnumerable GetPublicStaticMethods(this Type type) + { + return type.GetRuntimeMethods() + .Where(m => m.IsPublic && m.IsStatic); + } + + public static MethodInfo GetPrivateStaticMethod(this Type type, string name) + { + return type.GetRuntimeMethods() + .FirstOrDefault(m => !m.IsPublic && m.IsStatic && m.Name.Equals(name)) + ?? throw new MissingMethodException($"Expected to find a method named '{name}' in '{type.FullName}'."); + } + + public static MethodInfo? GetPublicStaticMethod(this Type type, string name, params Type[] parameterTypes) + { + return type.GetRuntimeMethods() + .FirstOrDefault(m => + { + if (m.IsPublic && m.IsStatic && m.Name.Equals(name)) + { + var parameters = m.GetParameters(); + return parameters.Length == parameterTypes.Length + && parameters.Zip(parameterTypes, (pi, pt) => pi.ParameterType == pt).All(r => r); + } + return false; + }); + } + + public static MethodInfo? GetPublicInstanceMethod(this Type type, string name) + { + return type.GetRuntimeMethods() + .FirstOrDefault(m => m.IsPublic && !m.IsStatic && m.Name.Equals(name)); + } + + public static MethodInfo? GetGetMethod(this PropertyInfo property, bool nonPublic) + { + var getter = property.GetMethod; + if (!nonPublic && !getter.IsPublic) + { + getter = null; + } + return getter; + } + + public static MethodInfo GetSetMethod(this PropertyInfo property) + { + return property.SetMethod; + } + + public static IEnumerable GetInterfaces(this Type type) + { + return type.GetTypeInfo().ImplementedInterfaces; + } + + public static Exception Unwrap(this TargetInvocationException ex) + { + return ex.InnerException; + } + + public static bool IsInstanceOf(this Type type, object o) + { + return o.GetType() == type || o.GetType().GetTypeInfo().IsSubclassOf(type); + } + + public static Attribute[] GetAllCustomAttributes(this PropertyInfo member) + { + // IMemberInfo.GetCustomAttributes ignores it's "inherit" parameter for properties, + // and the suggested replacement (Attribute.GetCustomAttributes) is not available + // on netstandard1.3 + var result = new List(); + var type = member.DeclaringType; + + while (type != null) + { + type.GetPublicProperty(member.Name); + result.AddRange(member.GetCustomAttributes(typeof(TAttribute))); + + type = type.BaseType(); + } + + return result.ToArray(); + } + } +} diff --git a/YamlDotNet/Portability/unitysubset3.5/exclude/PropertyInfoExtensions.cs b/YamlDotNet/Portability/unitysubset3.5/exclude/PropertyInfoExtensions.cs index 6660ff490..b834955f2 100644 --- a/YamlDotNet/Portability/unitysubset3.5/exclude/PropertyInfoExtensions.cs +++ b/YamlDotNet/Portability/unitysubset3.5/exclude/PropertyInfoExtensions.cs @@ -1,33 +1,33 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Reflection; - -namespace YamlDotNet -{ - internal static class PropertyInfoExtensions - { - public static object? ReadValue(this PropertyInfo property, object target) - { - return property.GetValue(target, null); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Reflection; + +namespace YamlDotNet +{ + internal static class PropertyInfoExtensions + { + public static object? ReadValue(this PropertyInfo property, object target) + { + return property.GetValue(target, null); + } + } +} diff --git a/YamlDotNet/Portability/unitysubset3.5/exclude/StandardRegexOptions.cs b/YamlDotNet/Portability/unitysubset3.5/exclude/StandardRegexOptions.cs index 40490476c..7f2851fb2 100644 --- a/YamlDotNet/Portability/unitysubset3.5/exclude/StandardRegexOptions.cs +++ b/YamlDotNet/Portability/unitysubset3.5/exclude/StandardRegexOptions.cs @@ -1,30 +1,30 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Text.RegularExpressions; - -namespace YamlDotNet -{ - internal static class StandardRegexOptions - { - public const RegexOptions Compiled = RegexOptions.Compiled; - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Text.RegularExpressions; + +namespace YamlDotNet +{ + internal static class StandardRegexOptions + { + public const RegexOptions Compiled = RegexOptions.Compiled; + } +} diff --git a/YamlDotNet/Portability/unitysubset3.5/include/ExcludeFromCodeCoverageAttribute.cs b/YamlDotNet/Portability/unitysubset3.5/include/ExcludeFromCodeCoverageAttribute.cs index 519bf5ccd..d3b208716 100644 --- a/YamlDotNet/Portability/unitysubset3.5/include/ExcludeFromCodeCoverageAttribute.cs +++ b/YamlDotNet/Portability/unitysubset3.5/include/ExcludeFromCodeCoverageAttribute.cs @@ -1,31 +1,31 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Text; - -namespace System.Diagnostics.CodeAnalysis -{ - public sealed class ExcludeFromCodeCoverageAttribute : Attribute - { - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Diagnostics.CodeAnalysis +{ + public sealed class ExcludeFromCodeCoverageAttribute : Attribute + { + } +} diff --git a/YamlDotNet/Portability/unitysubset3.5/include/PropertyInfoExtensions.cs b/YamlDotNet/Portability/unitysubset3.5/include/PropertyInfoExtensions.cs index b7c85277b..eefd1381f 100644 --- a/YamlDotNet/Portability/unitysubset3.5/include/PropertyInfoExtensions.cs +++ b/YamlDotNet/Portability/unitysubset3.5/include/PropertyInfoExtensions.cs @@ -1,33 +1,33 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Reflection; - -namespace YamlDotNet -{ - internal static class PropertyInfoExtensions - { - public static object? ReadValue(this PropertyInfo property, object target) - { - return property.GetGetMethod().Invoke(target, null); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Reflection; + +namespace YamlDotNet +{ + internal static class PropertyInfoExtensions + { + public static object? ReadValue(this PropertyInfo property, object target) + { + return property.GetGetMethod().Invoke(target, null); + } + } +} diff --git a/YamlDotNet/Portability/unitysubset3.5/include/StandardRegexOptions.cs b/YamlDotNet/Portability/unitysubset3.5/include/StandardRegexOptions.cs index 36f11e9d3..da727680c 100644 --- a/YamlDotNet/Portability/unitysubset3.5/include/StandardRegexOptions.cs +++ b/YamlDotNet/Portability/unitysubset3.5/include/StandardRegexOptions.cs @@ -1,30 +1,30 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Text.RegularExpressions; - -namespace YamlDotNet -{ - internal static class StandardRegexOptions - { - public const RegexOptions Compiled = RegexOptions.None; - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Text.RegularExpressions; + +namespace YamlDotNet +{ + internal static class StandardRegexOptions + { + public const RegexOptions Compiled = RegexOptions.None; + } +} diff --git a/YamlDotNet/Portability/unitysubset3.5/include/TargetFrameworkAttribute.cs b/YamlDotNet/Portability/unitysubset3.5/include/TargetFrameworkAttribute.cs index 37c068f9b..a9b6d4919 100644 --- a/YamlDotNet/Portability/unitysubset3.5/include/TargetFrameworkAttribute.cs +++ b/YamlDotNet/Portability/unitysubset3.5/include/TargetFrameworkAttribute.cs @@ -1,38 +1,38 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace System.Runtime.Versioning -{ - // Unity uses the net40 target, because that simplifies the project configuration. - // But in practice it targets framework 3.5, which does not have the TargetFrameworkAttribute - // that is added to the generated AssemblyInfo. - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] - internal sealed class TargetFrameworkAttribute : Attribute - { - public string FrameworkName { get; set; } - public string? FrameworkDisplayName { get; set; } - - public TargetFrameworkAttribute(string frameworkName) - { - FrameworkName = frameworkName; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace System.Runtime.Versioning +{ + // Unity uses the net40 target, because that simplifies the project configuration. + // But in practice it targets framework 3.5, which does not have the TargetFrameworkAttribute + // that is added to the generated AssemblyInfo. + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + internal sealed class TargetFrameworkAttribute : Attribute + { + public string FrameworkName { get; set; } + public string? FrameworkDisplayName { get; set; } + + public TargetFrameworkAttribute(string frameworkName) + { + FrameworkName = frameworkName; + } + } +} diff --git a/YamlDotNet/Properties/CustomAssemblyInfo.cs b/YamlDotNet/Properties/CustomAssemblyInfo.cs index 384f67630..b5c3d193f 100644 --- a/YamlDotNet/Properties/CustomAssemblyInfo.cs +++ b/YamlDotNet/Properties/CustomAssemblyInfo.cs @@ -1,40 +1,40 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !UNITY -using System; -using System.Reflection; -using System.Runtime.CompilerServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("YamlDotNet")] -[assembly: AssemblyDescription("The YamlDotNet library.")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("YamlDotNet")] -[assembly: AssemblyCopyright("Copyright (c) Antoine Aubry and contributors 2008 - 2019")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: CLSCompliant(true)] -[assembly: InternalsVisibleTo("YamlDotNet.Test, PublicKey=002400000480000094000000060200000024000052534131000400000100010065e52a453dde5c5b4be5bbe2205755727fce80244b79b894faf8793d80f7db9a96d360b51c220782db32aacee4cb5b8a91bee33aeec700e1f21895c4baadef501eeeac609220d1651603b378173811ee5bb6a002df973d38821bd2fef820c00c174a69faec326a1983b570f07ec66147026b9c8753465de3a8d0c44b613b02af")] -#endif +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if !UNITY +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("YamlDotNet")] +[assembly: AssemblyDescription("The YamlDotNet library.")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("YamlDotNet")] +[assembly: AssemblyCopyright("Copyright (c) Antoine Aubry and contributors 2008 - 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: CLSCompliant(true)] +[assembly: InternalsVisibleTo("YamlDotNet.Test, PublicKey=002400000480000094000000060200000024000052534131000400000100010065e52a453dde5c5b4be5bbe2205755727fce80244b79b894faf8793d80f7db9a96d360b51c220782db32aacee4cb5b8a91bee33aeec700e1f21895c4baadef501eeeac609220d1651603b378173811ee5bb6a002df973d38821bd2fef820c00c174a69faec326a1983b570f07ec66147026b9c8753465de3a8d0c44b613b02af")] +#endif diff --git a/YamlDotNet/RepresentationModel/DocumentLoadingState.cs b/YamlDotNet/RepresentationModel/DocumentLoadingState.cs index e424c39be..2f506a1f2 100644 --- a/YamlDotNet/RepresentationModel/DocumentLoadingState.cs +++ b/YamlDotNet/RepresentationModel/DocumentLoadingState.cs @@ -1,111 +1,111 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using YamlDotNet.Core; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Manages the state of a while it is loading. - /// - internal class DocumentLoadingState - { - private readonly IDictionary anchors = new Dictionary(); - private readonly IList nodesWithUnresolvedAliases = new List(); - - /// - /// Adds the specified node to the anchor list. - /// - /// The node. - public void AddAnchor(YamlNode node) - { - if (node.Anchor.IsEmpty) - { - throw new ArgumentException("The specified node does not have an anchor"); - } - - if (anchors.ContainsKey(node.Anchor)) - { - anchors[node.Anchor] = node; - } - else - { - anchors.Add(node.Anchor, node); - } - } - - /// - /// Gets the node with the specified anchor. - /// - /// The anchor. - /// The start position. - /// The end position. - /// - /// if there is no node with that anchor. - public YamlNode GetNode(AnchorName anchor, Mark start, Mark end) - { - if (anchors.TryGetValue(anchor, out var target)) - { - return target; - } - else - { - throw new AnchorNotFoundException(start, end, $"The anchor '{anchor}' does not exists"); - } - } - - /// - /// Gets the node with the specified anchor. - /// - /// The anchor. - /// The node that was retrieved. - /// true if the anchor was found; otherwise false. - public bool TryGetNode(AnchorName anchor, [NotNullWhen(true)] out YamlNode? node) - { - return anchors.TryGetValue(anchor, out node); - } - - /// - /// Adds the specified node to the collection of nodes with unresolved aliases. - /// - /// - /// The that has unresolved aliases. - /// - public void AddNodeWithUnresolvedAliases(YamlNode node) - { - nodesWithUnresolvedAliases.Add(node); - } - - /// - /// Resolves the aliases that could not be resolved while loading the document. - /// - public void ResolveAliases() - { - foreach (var node in nodesWithUnresolvedAliases) - { - node.ResolveAliases(this); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using YamlDotNet.Core; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Manages the state of a while it is loading. + /// + internal class DocumentLoadingState + { + private readonly IDictionary anchors = new Dictionary(); + private readonly IList nodesWithUnresolvedAliases = new List(); + + /// + /// Adds the specified node to the anchor list. + /// + /// The node. + public void AddAnchor(YamlNode node) + { + if (node.Anchor.IsEmpty) + { + throw new ArgumentException("The specified node does not have an anchor"); + } + + if (anchors.ContainsKey(node.Anchor)) + { + anchors[node.Anchor] = node; + } + else + { + anchors.Add(node.Anchor, node); + } + } + + /// + /// Gets the node with the specified anchor. + /// + /// The anchor. + /// The start position. + /// The end position. + /// + /// if there is no node with that anchor. + public YamlNode GetNode(AnchorName anchor, Mark start, Mark end) + { + if (anchors.TryGetValue(anchor, out var target)) + { + return target; + } + else + { + throw new AnchorNotFoundException(start, end, $"The anchor '{anchor}' does not exists"); + } + } + + /// + /// Gets the node with the specified anchor. + /// + /// The anchor. + /// The node that was retrieved. + /// true if the anchor was found; otherwise false. + public bool TryGetNode(AnchorName anchor, [NotNullWhen(true)] out YamlNode? node) + { + return anchors.TryGetValue(anchor, out node); + } + + /// + /// Adds the specified node to the collection of nodes with unresolved aliases. + /// + /// + /// The that has unresolved aliases. + /// + public void AddNodeWithUnresolvedAliases(YamlNode node) + { + nodesWithUnresolvedAliases.Add(node); + } + + /// + /// Resolves the aliases that could not be resolved while loading the document. + /// + public void ResolveAliases() + { + foreach (var node in nodesWithUnresolvedAliases) + { + node.ResolveAliases(this); + } + } + } +} diff --git a/YamlDotNet/RepresentationModel/EmitterState.cs b/YamlDotNet/RepresentationModel/EmitterState.cs index fed096cce..996c94c65 100644 --- a/YamlDotNet/RepresentationModel/EmitterState.cs +++ b/YamlDotNet/RepresentationModel/EmitterState.cs @@ -1,38 +1,38 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using YamlDotNet.Core; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Holds state that is used when emitting a stream. - /// - internal class EmitterState - { - /// - /// Gets the already emitted anchors. - /// - /// The emitted anchors. - public HashSet EmittedAnchors { get; } = new HashSet(); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using YamlDotNet.Core; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Holds state that is used when emitting a stream. + /// + internal class EmitterState + { + /// + /// Gets the already emitted anchors. + /// + /// The emitted anchors. + public HashSet EmittedAnchors { get; } = new HashSet(); + } +} diff --git a/YamlDotNet/RepresentationModel/IYamlVisitor.cs b/YamlDotNet/RepresentationModel/IYamlVisitor.cs index 2726c75b0..57a4dcab0 100644 --- a/YamlDotNet/RepresentationModel/IYamlVisitor.cs +++ b/YamlDotNet/RepresentationModel/IYamlVisitor.cs @@ -1,69 +1,69 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Defines the method needed to be able to visit Yaml elements. - /// - public interface IYamlVisitor - { - /// - /// Visits a . - /// - /// - /// The that is being visited. - /// - void Visit(YamlStream stream); - - /// - /// Visits a . - /// - /// - /// The that is being visited. - /// - void Visit(YamlDocument document); - - /// - /// Visits a . - /// - /// - /// The that is being visited. - /// - void Visit(YamlScalarNode scalar); - - /// - /// Visits a . - /// - /// - /// The that is being visited. - /// - void Visit(YamlSequenceNode sequence); - - /// - /// Visits a . - /// - /// - /// The that is being visited. - /// - void Visit(YamlMappingNode mapping); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Defines the method needed to be able to visit Yaml elements. + /// + public interface IYamlVisitor + { + /// + /// Visits a . + /// + /// + /// The that is being visited. + /// + void Visit(YamlStream stream); + + /// + /// Visits a . + /// + /// + /// The that is being visited. + /// + void Visit(YamlDocument document); + + /// + /// Visits a . + /// + /// + /// The that is being visited. + /// + void Visit(YamlScalarNode scalar); + + /// + /// Visits a . + /// + /// + /// The that is being visited. + /// + void Visit(YamlSequenceNode sequence); + + /// + /// Visits a . + /// + /// + /// The that is being visited. + /// + void Visit(YamlMappingNode mapping); + } +} diff --git a/YamlDotNet/RepresentationModel/LibYamlEventStream.cs b/YamlDotNet/RepresentationModel/LibYamlEventStream.cs index 91e1d1d67..7d50e4f12 100644 --- a/YamlDotNet/RepresentationModel/LibYamlEventStream.cs +++ b/YamlDotNet/RepresentationModel/LibYamlEventStream.cs @@ -1,136 +1,136 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Represents a LibYAML event stream. - /// - public class LibYamlEventStream - { - private readonly IParser parser; - - /// - /// Initializes a new instance of the class - /// from the specified . - /// - public LibYamlEventStream(IParser iParser) - { - parser = iParser ?? throw new ArgumentNullException(nameof(iParser)); - } - - public void WriteTo(TextWriter textWriter) - { - while (parser.MoveNext()) - { - switch (parser.Current) - { - case AnchorAlias anchorAlias: - textWriter.Write("=ALI *"); - textWriter.Write(anchorAlias.Value); - break; - case DocumentEnd documentEnd: - textWriter.Write("-DOC"); - if (!documentEnd.IsImplicit) - { - textWriter.Write(" ..."); - } - break; - case DocumentStart documentStart: - textWriter.Write("+DOC"); - if (!documentStart.IsImplicit) - { - textWriter.Write(" ---"); - } - break; - case MappingEnd _: - textWriter.Write("-MAP"); - break; - case MappingStart mappingStart: - textWriter.Write("+MAP"); - WriteAnchorAndTag(textWriter, mappingStart); - break; - case Scalar scalar: - textWriter.Write("=VAL"); - WriteAnchorAndTag(textWriter, scalar); - - switch (scalar.Style) - { - case ScalarStyle.DoubleQuoted: textWriter.Write(" \""); break; - case ScalarStyle.SingleQuoted: textWriter.Write(" '"); break; - case ScalarStyle.Folded: textWriter.Write(" >"); break; - case ScalarStyle.Literal: textWriter.Write(" |"); break; - default: textWriter.Write(" :"); break; - } - - foreach (var character in scalar.Value) - { - switch (character) - { - case '\b': textWriter.Write("\\b"); break; - case '\t': textWriter.Write("\\t"); break; - case '\n': textWriter.Write("\\n"); break; - case '\r': textWriter.Write("\\r"); break; - case '\\': textWriter.Write("\\\\"); break; - default: textWriter.Write(character); break; - } - } - break; - case SequenceEnd _: - textWriter.Write("-SEQ"); - break; - case SequenceStart sequenceStart: - textWriter.Write("+SEQ"); - WriteAnchorAndTag(textWriter, sequenceStart); - break; - case StreamEnd _: - textWriter.Write("-STR"); - break; - case StreamStart _: - textWriter.Write("+STR"); - break; - } - textWriter.WriteLine(); - } - } - - private void WriteAnchorAndTag(TextWriter textWriter, NodeEvent nodeEvent) - { - if (!nodeEvent.Anchor.IsEmpty) - { - textWriter.Write(" &"); - textWriter.Write(nodeEvent.Anchor); - } - - if (!nodeEvent.Tag.IsEmpty) - { - textWriter.Write(" <"); - textWriter.Write(nodeEvent.Tag.Value); - textWriter.Write(">"); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.IO; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Represents a LibYAML event stream. + /// + public class LibYamlEventStream + { + private readonly IParser parser; + + /// + /// Initializes a new instance of the class + /// from the specified . + /// + public LibYamlEventStream(IParser iParser) + { + parser = iParser ?? throw new ArgumentNullException(nameof(iParser)); + } + + public void WriteTo(TextWriter textWriter) + { + while (parser.MoveNext()) + { + switch (parser.Current) + { + case AnchorAlias anchorAlias: + textWriter.Write("=ALI *"); + textWriter.Write(anchorAlias.Value); + break; + case DocumentEnd documentEnd: + textWriter.Write("-DOC"); + if (!documentEnd.IsImplicit) + { + textWriter.Write(" ..."); + } + break; + case DocumentStart documentStart: + textWriter.Write("+DOC"); + if (!documentStart.IsImplicit) + { + textWriter.Write(" ---"); + } + break; + case MappingEnd _: + textWriter.Write("-MAP"); + break; + case MappingStart mappingStart: + textWriter.Write("+MAP"); + WriteAnchorAndTag(textWriter, mappingStart); + break; + case Scalar scalar: + textWriter.Write("=VAL"); + WriteAnchorAndTag(textWriter, scalar); + + switch (scalar.Style) + { + case ScalarStyle.DoubleQuoted: textWriter.Write(" \""); break; + case ScalarStyle.SingleQuoted: textWriter.Write(" '"); break; + case ScalarStyle.Folded: textWriter.Write(" >"); break; + case ScalarStyle.Literal: textWriter.Write(" |"); break; + default: textWriter.Write(" :"); break; + } + + foreach (var character in scalar.Value) + { + switch (character) + { + case '\b': textWriter.Write("\\b"); break; + case '\t': textWriter.Write("\\t"); break; + case '\n': textWriter.Write("\\n"); break; + case '\r': textWriter.Write("\\r"); break; + case '\\': textWriter.Write("\\\\"); break; + default: textWriter.Write(character); break; + } + } + break; + case SequenceEnd _: + textWriter.Write("-SEQ"); + break; + case SequenceStart sequenceStart: + textWriter.Write("+SEQ"); + WriteAnchorAndTag(textWriter, sequenceStart); + break; + case StreamEnd _: + textWriter.Write("-STR"); + break; + case StreamStart _: + textWriter.Write("+STR"); + break; + } + textWriter.WriteLine(); + } + } + + private void WriteAnchorAndTag(TextWriter textWriter, NodeEvent nodeEvent) + { + if (!nodeEvent.Anchor.IsEmpty) + { + textWriter.Write(" &"); + textWriter.Write(nodeEvent.Anchor); + } + + if (!nodeEvent.Tag.IsEmpty) + { + textWriter.Write(" <"); + textWriter.Write(nodeEvent.Tag.Value); + textWriter.Write(">"); + } + } + } +} diff --git a/YamlDotNet/RepresentationModel/YamlAliasNode.cs b/YamlDotNet/RepresentationModel/YamlAliasNode.cs index c91e1e3e1..08638d86b 100644 --- a/YamlDotNet/RepresentationModel/YamlAliasNode.cs +++ b/YamlDotNet/RepresentationModel/YamlAliasNode.cs @@ -1,120 +1,120 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Represents an alias node in the YAML document. - /// - internal class YamlAliasNode : YamlNode - { - /// - /// Initializes a new instance of the class. - /// - /// The anchor. - internal YamlAliasNode(AnchorName anchor) - { - Anchor = anchor; - } - - /// - /// Resolves the aliases that could not be resolved when the node was created. - /// - /// The state of the document. - internal override void ResolveAliases(DocumentLoadingState state) - { - throw new NotSupportedException("Resolving an alias on an alias node does not make sense"); - } - - /// - /// Saves the current node to the specified emitter. - /// - /// The emitter where the node is to be saved. - /// The state. - internal override void Emit(IEmitter emitter, EmitterState state) - { - throw new NotSupportedException("A YamlAliasNode is an implementation detail and should never be saved."); - } - - /// - /// Accepts the specified visitor by calling the appropriate Visit method on it. - /// - /// - /// A . - /// - public override void Accept(IYamlVisitor visitor) - { - throw new NotSupportedException("A YamlAliasNode is an implementation detail and should never be visited."); - } - - /// - public override bool Equals(object? obj) - { - return obj is YamlAliasNode other - && Equals(other) - && Equals(Anchor, other.Anchor); - } - - /// - /// Serves as a hash function for a particular type. - /// - /// - /// A hash code for the current . - /// - public override int GetHashCode() - { - return base.GetHashCode(); - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - internal override string ToString(RecursionLevel level) - { - return "*" + Anchor; - } - - /// - /// Recursively enumerates all the nodes from the document, starting on the current node, - /// and throwing - /// if is reached. - /// - internal override IEnumerable SafeAllNodes(RecursionLevel level) - { - yield return this; - } - - /// - /// Gets the type of node. - /// - public override YamlNodeType NodeType - { - get { return YamlNodeType.Alias; } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Represents an alias node in the YAML document. + /// + internal class YamlAliasNode : YamlNode + { + /// + /// Initializes a new instance of the class. + /// + /// The anchor. + internal YamlAliasNode(AnchorName anchor) + { + Anchor = anchor; + } + + /// + /// Resolves the aliases that could not be resolved when the node was created. + /// + /// The state of the document. + internal override void ResolveAliases(DocumentLoadingState state) + { + throw new NotSupportedException("Resolving an alias on an alias node does not make sense"); + } + + /// + /// Saves the current node to the specified emitter. + /// + /// The emitter where the node is to be saved. + /// The state. + internal override void Emit(IEmitter emitter, EmitterState state) + { + throw new NotSupportedException("A YamlAliasNode is an implementation detail and should never be saved."); + } + + /// + /// Accepts the specified visitor by calling the appropriate Visit method on it. + /// + /// + /// A . + /// + public override void Accept(IYamlVisitor visitor) + { + throw new NotSupportedException("A YamlAliasNode is an implementation detail and should never be visited."); + } + + /// + public override bool Equals(object? obj) + { + return obj is YamlAliasNode other + && Equals(other) + && Equals(Anchor, other.Anchor); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + internal override string ToString(RecursionLevel level) + { + return "*" + Anchor; + } + + /// + /// Recursively enumerates all the nodes from the document, starting on the current node, + /// and throwing + /// if is reached. + /// + internal override IEnumerable SafeAllNodes(RecursionLevel level) + { + yield return this; + } + + /// + /// Gets the type of node. + /// + public override YamlNodeType NodeType + { + get { return YamlNodeType.Alias; } + } + } +} diff --git a/YamlDotNet/RepresentationModel/YamlDocument.cs b/YamlDotNet/RepresentationModel/YamlDocument.cs index 839291500..9c2ce078b 100644 --- a/YamlDotNet/RepresentationModel/YamlDocument.cs +++ b/YamlDotNet/RepresentationModel/YamlDocument.cs @@ -1,214 +1,214 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Represents an YAML document. - /// - public class YamlDocument - { - /// - /// Gets or sets the root node. - /// - /// The root node. - public YamlNode RootNode { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public YamlDocument(YamlNode rootNode) - { - RootNode = rootNode; - } - - /// - /// Initializes a new instance of the class with a single scalar node. - /// - public YamlDocument(string rootNode) - { - RootNode = new YamlScalarNode(rootNode); - } - - /// - /// Initializes a new instance of the class. - /// - internal YamlDocument(IParser parser) - { - var state = new DocumentLoadingState(); - - parser.Consume(); - - while (!parser.TryConsume(out var _)) - { - Debug.Assert(RootNode == null); - RootNode = YamlNode.ParseNode(parser, state); - - if (RootNode is YamlAliasNode) - { - throw new YamlException("A document cannot contain only an alias"); - } - } - - state.ResolveAliases(); - - if (RootNode == null) - { - // This should not happen unless the parser has a bug - throw new ArgumentException("Atempted to parse an empty document"); - } - } - - /// - /// Visitor that assigns anchors to nodes that are referenced more than once. - /// Existing anchors are preserved as much as possible. - /// - private class AnchorAssigningVisitor : YamlVisitorBase - { - private readonly HashSet existingAnchors = new HashSet(); - /// - /// Key: Node, Value: IsDuplicate - /// - private readonly Dictionary visitedNodes = new Dictionary(); - - public void AssignAnchors(YamlDocument document) - { - existingAnchors.Clear(); - visitedNodes.Clear(); - - document.Accept(this); - - var random = new Random(); - foreach (var visitedNode in visitedNodes) - { - if (visitedNode.Value) - { - AnchorName anchor; - // If the existing anchor is not already used, we can have it - if (!visitedNode.Key.Anchor.IsEmpty && !existingAnchors.Contains(visitedNode.Key.Anchor)) - { - anchor = visitedNode.Key.Anchor; - } - else - { - do - { - anchor = new AnchorName(random.Next().ToString(CultureInfo.InvariantCulture)); - } while (existingAnchors.Contains(anchor)); - } - - existingAnchors.Add(anchor); - visitedNode.Key.Anchor = anchor; - } - } - } - - /// - /// Returns whether the visited node is a duplicate. - /// - private bool VisitNodeAndFindDuplicates(YamlNode node) - { - if (visitedNodes.TryGetValue(node, out var isDuplicate)) - { - if (!isDuplicate) - { - visitedNodes[node] = true; - } - return !isDuplicate; - } - else - { - visitedNodes.Add(node, false); - return false; - } - } - - public override void Visit(YamlScalarNode scalar) - { - VisitNodeAndFindDuplicates(scalar); - } - - public override void Visit(YamlMappingNode mapping) - { - if (!VisitNodeAndFindDuplicates(mapping)) - { - base.Visit(mapping); - } - } - - public override void Visit(YamlSequenceNode sequence) - { - if (!VisitNodeAndFindDuplicates(sequence)) - { - base.Visit(sequence); - } - } - } - - private void AssignAnchors() - { - var visitor = new AnchorAssigningVisitor(); - visitor.AssignAnchors(this); - } - - internal void Save(IEmitter emitter, bool assignAnchors = true) - { - if (assignAnchors) - { - AssignAnchors(); - } - - emitter.Emit(new DocumentStart()); - RootNode.Save(emitter, new EmitterState()); - emitter.Emit(new DocumentEnd(false)); - } - - /// - /// Accepts the specified visitor by calling the appropriate Visit method on it. - /// - /// - /// A . - /// - public void Accept(IYamlVisitor visitor) - { - visitor.Visit(this); - } - - /// - /// Gets all nodes from the document. - /// is thrown if an infinite recursion is detected. - /// - public IEnumerable AllNodes - { - get - { - return RootNode.AllNodes; - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Represents an YAML document. + /// + public class YamlDocument + { + /// + /// Gets or sets the root node. + /// + /// The root node. + public YamlNode RootNode { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public YamlDocument(YamlNode rootNode) + { + RootNode = rootNode; + } + + /// + /// Initializes a new instance of the class with a single scalar node. + /// + public YamlDocument(string rootNode) + { + RootNode = new YamlScalarNode(rootNode); + } + + /// + /// Initializes a new instance of the class. + /// + internal YamlDocument(IParser parser) + { + var state = new DocumentLoadingState(); + + parser.Consume(); + + while (!parser.TryConsume(out var _)) + { + Debug.Assert(RootNode == null); + RootNode = YamlNode.ParseNode(parser, state); + + if (RootNode is YamlAliasNode) + { + throw new YamlException("A document cannot contain only an alias"); + } + } + + state.ResolveAliases(); + + if (RootNode == null) + { + // This should not happen unless the parser has a bug + throw new ArgumentException("Atempted to parse an empty document"); + } + } + + /// + /// Visitor that assigns anchors to nodes that are referenced more than once. + /// Existing anchors are preserved as much as possible. + /// + private class AnchorAssigningVisitor : YamlVisitorBase + { + private readonly HashSet existingAnchors = new HashSet(); + /// + /// Key: Node, Value: IsDuplicate + /// + private readonly Dictionary visitedNodes = new Dictionary(); + + public void AssignAnchors(YamlDocument document) + { + existingAnchors.Clear(); + visitedNodes.Clear(); + + document.Accept(this); + + var random = new Random(); + foreach (var visitedNode in visitedNodes) + { + if (visitedNode.Value) + { + AnchorName anchor; + // If the existing anchor is not already used, we can have it + if (!visitedNode.Key.Anchor.IsEmpty && !existingAnchors.Contains(visitedNode.Key.Anchor)) + { + anchor = visitedNode.Key.Anchor; + } + else + { + do + { + anchor = new AnchorName(random.Next().ToString(CultureInfo.InvariantCulture)); + } while (existingAnchors.Contains(anchor)); + } + + existingAnchors.Add(anchor); + visitedNode.Key.Anchor = anchor; + } + } + } + + /// + /// Returns whether the visited node is a duplicate. + /// + private bool VisitNodeAndFindDuplicates(YamlNode node) + { + if (visitedNodes.TryGetValue(node, out var isDuplicate)) + { + if (!isDuplicate) + { + visitedNodes[node] = true; + } + return !isDuplicate; + } + else + { + visitedNodes.Add(node, false); + return false; + } + } + + public override void Visit(YamlScalarNode scalar) + { + VisitNodeAndFindDuplicates(scalar); + } + + public override void Visit(YamlMappingNode mapping) + { + if (!VisitNodeAndFindDuplicates(mapping)) + { + base.Visit(mapping); + } + } + + public override void Visit(YamlSequenceNode sequence) + { + if (!VisitNodeAndFindDuplicates(sequence)) + { + base.Visit(sequence); + } + } + } + + private void AssignAnchors() + { + var visitor = new AnchorAssigningVisitor(); + visitor.AssignAnchors(this); + } + + internal void Save(IEmitter emitter, bool assignAnchors = true) + { + if (assignAnchors) + { + AssignAnchors(); + } + + emitter.Emit(new DocumentStart()); + RootNode.Save(emitter, new EmitterState()); + emitter.Emit(new DocumentEnd(false)); + } + + /// + /// Accepts the specified visitor by calling the appropriate Visit method on it. + /// + /// + /// A . + /// + public void Accept(IYamlVisitor visitor) + { + visitor.Visit(this); + } + + /// + /// Gets all nodes from the document. + /// is thrown if an infinite recursion is detected. + /// + public IEnumerable AllNodes + { + get + { + return RootNode.AllNodes; + } + } + } +} diff --git a/YamlDotNet/RepresentationModel/YamlMappingNode.cs b/YamlDotNet/RepresentationModel/YamlMappingNode.cs index ab8e1c246..3aa34d9ad 100644 --- a/YamlDotNet/RepresentationModel/YamlMappingNode.cs +++ b/YamlDotNet/RepresentationModel/YamlMappingNode.cs @@ -1,426 +1,426 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Helpers; -using YamlDotNet.Serialization; -using static YamlDotNet.Core.HashCode; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Represents a mapping node in the YAML document. - /// - public sealed class YamlMappingNode : YamlNode, IEnumerable>, IYamlConvertible - { - private readonly OrderedDictionary children = new OrderedDictionary(); - - /// - /// Gets the children of the current node. - /// - /// The children. - public IOrderedDictionary Children - { - get - { - return children; - } - } - - /// - /// Gets or sets the style of the node. - /// - /// The style. - public MappingStyle Style { get; set; } - - /// - /// Initializes a new instance of the class. - /// - internal YamlMappingNode(IParser parser, DocumentLoadingState state) - { - Load(parser, state); - } - - private void Load(IParser parser, DocumentLoadingState state) - { - var mapping = parser.Consume(); - Load(mapping, state); - Style = mapping.Style; - - var hasUnresolvedAliases = false; - while (!parser.TryConsume(out var _)) - { - var key = ParseNode(parser, state); - var value = ParseNode(parser, state); - - try - { - children.Add(key, value); - } - catch (ArgumentException err) - { - throw new YamlException(key.Start, key.End, "Duplicate key", err); - } - - hasUnresolvedAliases |= key is YamlAliasNode || value is YamlAliasNode; - } - - if (hasUnresolvedAliases) - { - state.AddNodeWithUnresolvedAliases(this); - } - } - - /// - /// Initializes a new instance of the class. - /// - public YamlMappingNode() - { - } - - /// - /// Initializes a new instance of the class. - /// - public YamlMappingNode(params KeyValuePair[] children) - : this((IEnumerable>)children) - { - } - - /// - /// Initializes a new instance of the class. - /// - public YamlMappingNode(IEnumerable> children) - { - foreach (var child in children) - { - this.children.Add(child); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// A sequence of where even elements are keys and odd elements are values. - public YamlMappingNode(params YamlNode[] children) - : this((IEnumerable)children) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A sequence of where even elements are keys and odd elements are values. - public YamlMappingNode(IEnumerable children) - { - using var enumerator = children.GetEnumerator(); - while (enumerator.MoveNext()) - { - var key = enumerator.Current; - if (!enumerator.MoveNext()) - { - throw new ArgumentException("When constructing a mapping node with a sequence, the number of elements of the sequence must be even."); - } - - Add(key, enumerator.Current); - } - } - - /// - /// Adds the specified mapping to the collection. - /// - /// The key node. - /// The value node. - public void Add(YamlNode key, YamlNode value) - { - children.Add(key, value); - } - - /// - /// Adds the specified mapping to the collection. - /// - /// The key node. - /// The value node. - public void Add(string key, YamlNode value) - { - children.Add(new YamlScalarNode(key), value); - } - - /// - /// Adds the specified mapping to the collection. - /// - /// The key node. - /// The value node. - public void Add(YamlNode key, string value) - { - children.Add(key, new YamlScalarNode(value)); - } - - /// - /// Adds the specified mapping to the collection. - /// - /// The key node. - /// The value node. - public void Add(string key, string value) - { - children.Add(new YamlScalarNode(key), new YamlScalarNode(value)); - } - - /// - /// Resolves the aliases that could not be resolved when the node was created. - /// - /// The state of the document. - internal override void ResolveAliases(DocumentLoadingState state) - { - Dictionary? keysToUpdate = null; - Dictionary? valuesToUpdate = null; - foreach (var entry in children) - { - if (entry.Key is YamlAliasNode) - { - if (keysToUpdate == null) - { - keysToUpdate = new Dictionary(); - } - // TODO: The representation model should be redesigned, because here the anchor could be null but that would be invalid YAML - keysToUpdate.Add(entry.Key, state.GetNode(entry.Key.Anchor!, entry.Key.Start, entry.Key.End)); - } - if (entry.Value is YamlAliasNode) - { - if (valuesToUpdate == null) - { - valuesToUpdate = new Dictionary(); - } - // TODO: The representation model should be redesigned, because here the anchor could be null but that would be invalid YAML - valuesToUpdate.Add(entry.Key, state.GetNode(entry.Value.Anchor!, entry.Value.Start, entry.Value.End)); - } - } - if (valuesToUpdate != null) - { - foreach (var entry in valuesToUpdate) - { - children[entry.Key] = entry.Value; - } - } - if (keysToUpdate != null) - { - foreach (var entry in keysToUpdate) - { - var value = children[entry.Key]; - children.Remove(entry.Key); - children.Add(entry.Value, value); - } - } - } - - /// - /// Saves the current node to the specified emitter. - /// - /// The emitter where the node is to be saved. - /// The state. - internal override void Emit(IEmitter emitter, EmitterState state) - { - emitter.Emit(new MappingStart(Anchor, Tag, true, Style)); - foreach (var entry in children) - { - entry.Key.Save(emitter, state); - entry.Value.Save(emitter, state); - } - emitter.Emit(new MappingEnd()); - } - - /// - /// Accepts the specified visitor by calling the appropriate Visit method on it. - /// - /// - /// A . - /// - public override void Accept(IYamlVisitor visitor) - { - visitor.Visit(this); - } - - /// - public override bool Equals(object? obj) - { - var other = obj as YamlMappingNode; - var areEqual = other != null - && Equals(Tag, other.Tag) - && children.Count == other.children.Count; - - if (!areEqual) - { - return false; - } - - foreach (var entry in children) - { - if (!other!.children.TryGetValue(entry.Key, out var otherNode) || !Equals(entry.Value, otherNode)) - { - return false; - } - } - - return true; - } - - /// - /// Serves as a hash function for a particular type. - /// - /// - /// A hash code for the current . - /// - public override int GetHashCode() - { - var hashCode = base.GetHashCode(); - - foreach (var entry in children) - { - hashCode = CombineHashCodes(hashCode, entry.Key); - hashCode = CombineHashCodes(hashCode, entry.Value); - } - return hashCode; - } - - /// - /// Recursively enumerates all the nodes from the document, starting on the current node, - /// and throwing - /// if is reached. - /// - internal override IEnumerable SafeAllNodes(RecursionLevel level) - { - level.Increment(); - yield return this; - foreach (var child in children) - { - foreach (var node in child.Key.SafeAllNodes(level)) - { - yield return node; - } - foreach (var node in child.Value.SafeAllNodes(level)) - { - yield return node; - } - } - level.Decrement(); - } - - /// - /// Gets the type of node. - /// - public override YamlNodeType NodeType - { - get { return YamlNodeType.Mapping; } - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - internal override string ToString(RecursionLevel level) - { - if (!level.TryIncrement()) - { - return MaximumRecursionLevelReachedToStringValue; - } - - using var textBuilder = StringBuilderPool.Rent(); - var text = textBuilder.Builder; - text.Append("{ "); - - foreach (var child in children) - { - if (text.Length > 2) - { - text.Append(", "); - } - text.Append("{ ").Append(child.Key.ToString(level)).Append(", ").Append(child.Value.ToString(level)).Append(" }"); - } - - text.Append(" }"); - - level.Decrement(); - - return text.ToString(); - } - - #region IEnumerable> Members - - /// - public IEnumerator> GetEnumerator() - { - return children.GetEnumerator(); - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) - { - Load(parser, new DocumentLoadingState()); - } - - void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) - { - Emit(emitter, new EmitterState()); - } - - /// - /// Creates a containing a key-value pair for each property of the specified object. - /// - public static YamlMappingNode FromObject(object mapping) - { - if (mapping == null) - { - throw new ArgumentNullException(nameof(mapping)); - } - - var result = new YamlMappingNode(); - foreach (var property in mapping.GetType().GetPublicProperties()) - { - // CanRead == true => GetGetMethod() != null - if (property.CanRead && property.GetGetMethod(false)!.GetParameters().Length == 0) - { - var value = property.GetValue(mapping, null); - if (!(value is YamlNode valueNode)) - { - var valueAsString = Convert.ToString(value); - valueNode = valueAsString ?? string.Empty; - } - result.Add(property.Name, valueNode); - } - } - return result; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Helpers; +using YamlDotNet.Serialization; +using static YamlDotNet.Core.HashCode; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Represents a mapping node in the YAML document. + /// + public sealed class YamlMappingNode : YamlNode, IEnumerable>, IYamlConvertible + { + private readonly OrderedDictionary children = new OrderedDictionary(); + + /// + /// Gets the children of the current node. + /// + /// The children. + public IOrderedDictionary Children + { + get + { + return children; + } + } + + /// + /// Gets or sets the style of the node. + /// + /// The style. + public MappingStyle Style { get; set; } + + /// + /// Initializes a new instance of the class. + /// + internal YamlMappingNode(IParser parser, DocumentLoadingState state) + { + Load(parser, state); + } + + private void Load(IParser parser, DocumentLoadingState state) + { + var mapping = parser.Consume(); + Load(mapping, state); + Style = mapping.Style; + + var hasUnresolvedAliases = false; + while (!parser.TryConsume(out var _)) + { + var key = ParseNode(parser, state); + var value = ParseNode(parser, state); + + try + { + children.Add(key, value); + } + catch (ArgumentException err) + { + throw new YamlException(key.Start, key.End, "Duplicate key", err); + } + + hasUnresolvedAliases |= key is YamlAliasNode || value is YamlAliasNode; + } + + if (hasUnresolvedAliases) + { + state.AddNodeWithUnresolvedAliases(this); + } + } + + /// + /// Initializes a new instance of the class. + /// + public YamlMappingNode() + { + } + + /// + /// Initializes a new instance of the class. + /// + public YamlMappingNode(params KeyValuePair[] children) + : this((IEnumerable>)children) + { + } + + /// + /// Initializes a new instance of the class. + /// + public YamlMappingNode(IEnumerable> children) + { + foreach (var child in children) + { + this.children.Add(child); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// A sequence of where even elements are keys and odd elements are values. + public YamlMappingNode(params YamlNode[] children) + : this((IEnumerable)children) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A sequence of where even elements are keys and odd elements are values. + public YamlMappingNode(IEnumerable children) + { + using var enumerator = children.GetEnumerator(); + while (enumerator.MoveNext()) + { + var key = enumerator.Current; + if (!enumerator.MoveNext()) + { + throw new ArgumentException("When constructing a mapping node with a sequence, the number of elements of the sequence must be even."); + } + + Add(key, enumerator.Current); + } + } + + /// + /// Adds the specified mapping to the collection. + /// + /// The key node. + /// The value node. + public void Add(YamlNode key, YamlNode value) + { + children.Add(key, value); + } + + /// + /// Adds the specified mapping to the collection. + /// + /// The key node. + /// The value node. + public void Add(string key, YamlNode value) + { + children.Add(new YamlScalarNode(key), value); + } + + /// + /// Adds the specified mapping to the collection. + /// + /// The key node. + /// The value node. + public void Add(YamlNode key, string value) + { + children.Add(key, new YamlScalarNode(value)); + } + + /// + /// Adds the specified mapping to the collection. + /// + /// The key node. + /// The value node. + public void Add(string key, string value) + { + children.Add(new YamlScalarNode(key), new YamlScalarNode(value)); + } + + /// + /// Resolves the aliases that could not be resolved when the node was created. + /// + /// The state of the document. + internal override void ResolveAliases(DocumentLoadingState state) + { + Dictionary? keysToUpdate = null; + Dictionary? valuesToUpdate = null; + foreach (var entry in children) + { + if (entry.Key is YamlAliasNode) + { + if (keysToUpdate == null) + { + keysToUpdate = new Dictionary(); + } + // TODO: The representation model should be redesigned, because here the anchor could be null but that would be invalid YAML + keysToUpdate.Add(entry.Key, state.GetNode(entry.Key.Anchor!, entry.Key.Start, entry.Key.End)); + } + if (entry.Value is YamlAliasNode) + { + if (valuesToUpdate == null) + { + valuesToUpdate = new Dictionary(); + } + // TODO: The representation model should be redesigned, because here the anchor could be null but that would be invalid YAML + valuesToUpdate.Add(entry.Key, state.GetNode(entry.Value.Anchor!, entry.Value.Start, entry.Value.End)); + } + } + if (valuesToUpdate != null) + { + foreach (var entry in valuesToUpdate) + { + children[entry.Key] = entry.Value; + } + } + if (keysToUpdate != null) + { + foreach (var entry in keysToUpdate) + { + var value = children[entry.Key]; + children.Remove(entry.Key); + children.Add(entry.Value, value); + } + } + } + + /// + /// Saves the current node to the specified emitter. + /// + /// The emitter where the node is to be saved. + /// The state. + internal override void Emit(IEmitter emitter, EmitterState state) + { + emitter.Emit(new MappingStart(Anchor, Tag, true, Style)); + foreach (var entry in children) + { + entry.Key.Save(emitter, state); + entry.Value.Save(emitter, state); + } + emitter.Emit(new MappingEnd()); + } + + /// + /// Accepts the specified visitor by calling the appropriate Visit method on it. + /// + /// + /// A . + /// + public override void Accept(IYamlVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override bool Equals(object? obj) + { + var other = obj as YamlMappingNode; + var areEqual = other != null + && Equals(Tag, other.Tag) + && children.Count == other.children.Count; + + if (!areEqual) + { + return false; + } + + foreach (var entry in children) + { + if (!other!.children.TryGetValue(entry.Key, out var otherNode) || !Equals(entry.Value, otherNode)) + { + return false; + } + } + + return true; + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + var hashCode = base.GetHashCode(); + + foreach (var entry in children) + { + hashCode = CombineHashCodes(hashCode, entry.Key); + hashCode = CombineHashCodes(hashCode, entry.Value); + } + return hashCode; + } + + /// + /// Recursively enumerates all the nodes from the document, starting on the current node, + /// and throwing + /// if is reached. + /// + internal override IEnumerable SafeAllNodes(RecursionLevel level) + { + level.Increment(); + yield return this; + foreach (var child in children) + { + foreach (var node in child.Key.SafeAllNodes(level)) + { + yield return node; + } + foreach (var node in child.Value.SafeAllNodes(level)) + { + yield return node; + } + } + level.Decrement(); + } + + /// + /// Gets the type of node. + /// + public override YamlNodeType NodeType + { + get { return YamlNodeType.Mapping; } + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + internal override string ToString(RecursionLevel level) + { + if (!level.TryIncrement()) + { + return MaximumRecursionLevelReachedToStringValue; + } + + using var textBuilder = StringBuilderPool.Rent(); + var text = textBuilder.Builder; + text.Append("{ "); + + foreach (var child in children) + { + if (text.Length > 2) + { + text.Append(", "); + } + text.Append("{ ").Append(child.Key.ToString(level)).Append(", ").Append(child.Value.ToString(level)).Append(" }"); + } + + text.Append(" }"); + + level.Decrement(); + + return text.ToString(); + } + + #region IEnumerable> Members + + /// + public IEnumerator> GetEnumerator() + { + return children.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) + { + Load(parser, new DocumentLoadingState()); + } + + void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) + { + Emit(emitter, new EmitterState()); + } + + /// + /// Creates a containing a key-value pair for each property of the specified object. + /// + public static YamlMappingNode FromObject(object mapping) + { + if (mapping == null) + { + throw new ArgumentNullException(nameof(mapping)); + } + + var result = new YamlMappingNode(); + foreach (var property in mapping.GetType().GetPublicProperties()) + { + // CanRead == true => GetGetMethod() != null + if (property.CanRead && property.GetGetMethod(false)!.GetParameters().Length == 0) + { + var value = property.GetValue(mapping, null); + if (!(value is YamlNode valueNode)) + { + var valueAsString = Convert.ToString(value); + valueNode = valueAsString ?? string.Empty; + } + result.Add(property.Name, valueNode); + } + } + return result; + } + } +} diff --git a/YamlDotNet/RepresentationModel/YamlNode.cs b/YamlDotNet/RepresentationModel/YamlNode.cs index 686dcdc5b..4c7885dea 100644 --- a/YamlDotNet/RepresentationModel/YamlNode.cs +++ b/YamlDotNet/RepresentationModel/YamlNode.cs @@ -1,236 +1,236 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Represents a single node in the YAML document. - /// - public abstract class YamlNode - { - private const int MaximumRecursionLevel = 1000; - internal const string MaximumRecursionLevelReachedToStringValue = "WARNING! INFINITE RECURSION!"; - - /// - /// Gets or sets the anchor of the node. - /// - /// The anchor. - public AnchorName Anchor { get; set; } - - /// - /// Gets or sets the tag of the node. - /// - /// The tag. - public TagName Tag { get; set; } - - /// - /// Gets the position in the input stream where the event that originated the node starts. - /// - public Mark Start { get; private set; } = Mark.Empty; - - /// - /// Gets the position in the input stream where the event that originated the node ends. - /// - public Mark End { get; private set; } = Mark.Empty; - - /// - /// Loads the specified event. - /// - /// The event. - /// The state of the document. - internal void Load(NodeEvent yamlEvent, DocumentLoadingState state) - { - Tag = yamlEvent.Tag; - if (!yamlEvent.Anchor.IsEmpty) - { - Anchor = yamlEvent.Anchor; - state.AddAnchor(this); - } - Start = yamlEvent.Start; - End = yamlEvent.End; - } - - /// - /// Parses the node represented by the next event in . - /// - /// Returns the node that has been parsed. - internal static YamlNode ParseNode(IParser parser, DocumentLoadingState state) - { - if (parser.Accept(out var _)) - { - return new YamlScalarNode(parser, state); - } - - if (parser.Accept(out var _)) - { - return new YamlSequenceNode(parser, state); - } - - if (parser.Accept(out var _)) - { - return new YamlMappingNode(parser, state); - } - - if (parser.TryConsume(out var alias)) - { - return state.TryGetNode(alias.Value, out var node) ? node : new YamlAliasNode(alias.Value); - } - - throw new ArgumentException("The current event is of an unsupported type.", nameof(parser)); - } - - /// - /// Resolves the aliases that could not be resolved when the node was created. - /// - /// The state of the document. - internal abstract void ResolveAliases(DocumentLoadingState state); - - /// - /// Saves the current node to the specified emitter. - /// - /// The emitter where the node is to be saved. - /// The state. - internal void Save(IEmitter emitter, EmitterState state) - { - if (!Anchor.IsEmpty && !state.EmittedAnchors.Add(Anchor)) - { - emitter.Emit(new AnchorAlias(Anchor)); - } - else - { - Emit(emitter, state); - } - } - - /// - /// Saves the current node to the specified emitter. - /// - /// The emitter where the node is to be saved. - /// The state. - internal abstract void Emit(IEmitter emitter, EmitterState state); - - /// - /// Accepts the specified visitor by calling the appropriate Visit method on it. - /// - /// - /// A . - /// - public abstract void Accept(IYamlVisitor visitor); - - public override string ToString() - { - var level = new RecursionLevel(MaximumRecursionLevel); - return ToString(level); - } - - internal abstract string ToString(RecursionLevel level); - - /// - /// Gets all nodes from the document, starting on the current node. - /// is thrown if an infinite recursion is detected. - /// - public IEnumerable AllNodes - { - get - { - var level = new RecursionLevel(MaximumRecursionLevel); - return SafeAllNodes(level); - } - } - - /// - /// When implemented, recursively enumerates all the nodes from the document, starting on the current node. - /// If is reached, a is thrown - /// instead of continuing and crashing with a . - /// - internal abstract IEnumerable SafeAllNodes(RecursionLevel level); - - /// - /// Gets the type of node. - /// - public abstract YamlNodeType NodeType - { - get; - } - - /// - /// Performs an implicit conversion from to . - /// - /// The value. - /// The result of the conversion. - public static implicit operator YamlNode(string value) - { - return new YamlScalarNode(value); - } - - /// - /// Performs an implicit conversion from string[] to . - /// - /// The value. - /// The result of the conversion. - public static implicit operator YamlNode(string[] sequence) - { - return new YamlSequenceNode(sequence.Select(i => (YamlNode)i)); - } - - /// - /// Converts a to a string by returning its value. - /// - public static explicit operator string?(YamlNode node) - { - return node is YamlScalarNode scalar - ? scalar.Value - : throw new ArgumentException($"Attempted to convert a '{node.NodeType}' to string. This conversion is valid only for Scalars."); - } - - /// - /// Gets the nth element in a . - /// - public YamlNode this[int index] - { - get - { - return this is YamlSequenceNode sequence - ? sequence.Children[index] - : throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {index}. Only Sequences can be indexed by number."); - } - } - - /// - /// Gets the value associated with a key in a . - /// - public YamlNode this[YamlNode key] - { - get - { - return this is YamlMappingNode mapping - ? mapping.Children[key] - : throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {key}. Only Mappings can be indexed by key."); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Represents a single node in the YAML document. + /// + public abstract class YamlNode + { + private const int MaximumRecursionLevel = 1000; + internal const string MaximumRecursionLevelReachedToStringValue = "WARNING! INFINITE RECURSION!"; + + /// + /// Gets or sets the anchor of the node. + /// + /// The anchor. + public AnchorName Anchor { get; set; } + + /// + /// Gets or sets the tag of the node. + /// + /// The tag. + public TagName Tag { get; set; } + + /// + /// Gets the position in the input stream where the event that originated the node starts. + /// + public Mark Start { get; private set; } = Mark.Empty; + + /// + /// Gets the position in the input stream where the event that originated the node ends. + /// + public Mark End { get; private set; } = Mark.Empty; + + /// + /// Loads the specified event. + /// + /// The event. + /// The state of the document. + internal void Load(NodeEvent yamlEvent, DocumentLoadingState state) + { + Tag = yamlEvent.Tag; + if (!yamlEvent.Anchor.IsEmpty) + { + Anchor = yamlEvent.Anchor; + state.AddAnchor(this); + } + Start = yamlEvent.Start; + End = yamlEvent.End; + } + + /// + /// Parses the node represented by the next event in . + /// + /// Returns the node that has been parsed. + internal static YamlNode ParseNode(IParser parser, DocumentLoadingState state) + { + if (parser.Accept(out var _)) + { + return new YamlScalarNode(parser, state); + } + + if (parser.Accept(out var _)) + { + return new YamlSequenceNode(parser, state); + } + + if (parser.Accept(out var _)) + { + return new YamlMappingNode(parser, state); + } + + if (parser.TryConsume(out var alias)) + { + return state.TryGetNode(alias.Value, out var node) ? node : new YamlAliasNode(alias.Value); + } + + throw new ArgumentException("The current event is of an unsupported type.", nameof(parser)); + } + + /// + /// Resolves the aliases that could not be resolved when the node was created. + /// + /// The state of the document. + internal abstract void ResolveAliases(DocumentLoadingState state); + + /// + /// Saves the current node to the specified emitter. + /// + /// The emitter where the node is to be saved. + /// The state. + internal void Save(IEmitter emitter, EmitterState state) + { + if (!Anchor.IsEmpty && !state.EmittedAnchors.Add(Anchor)) + { + emitter.Emit(new AnchorAlias(Anchor)); + } + else + { + Emit(emitter, state); + } + } + + /// + /// Saves the current node to the specified emitter. + /// + /// The emitter where the node is to be saved. + /// The state. + internal abstract void Emit(IEmitter emitter, EmitterState state); + + /// + /// Accepts the specified visitor by calling the appropriate Visit method on it. + /// + /// + /// A . + /// + public abstract void Accept(IYamlVisitor visitor); + + public override string ToString() + { + var level = new RecursionLevel(MaximumRecursionLevel); + return ToString(level); + } + + internal abstract string ToString(RecursionLevel level); + + /// + /// Gets all nodes from the document, starting on the current node. + /// is thrown if an infinite recursion is detected. + /// + public IEnumerable AllNodes + { + get + { + var level = new RecursionLevel(MaximumRecursionLevel); + return SafeAllNodes(level); + } + } + + /// + /// When implemented, recursively enumerates all the nodes from the document, starting on the current node. + /// If is reached, a is thrown + /// instead of continuing and crashing with a . + /// + internal abstract IEnumerable SafeAllNodes(RecursionLevel level); + + /// + /// Gets the type of node. + /// + public abstract YamlNodeType NodeType + { + get; + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator YamlNode(string value) + { + return new YamlScalarNode(value); + } + + /// + /// Performs an implicit conversion from string[] to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator YamlNode(string[] sequence) + { + return new YamlSequenceNode(sequence.Select(i => (YamlNode)i)); + } + + /// + /// Converts a to a string by returning its value. + /// + public static explicit operator string?(YamlNode node) + { + return node is YamlScalarNode scalar + ? scalar.Value + : throw new ArgumentException($"Attempted to convert a '{node.NodeType}' to string. This conversion is valid only for Scalars."); + } + + /// + /// Gets the nth element in a . + /// + public YamlNode this[int index] + { + get + { + return this is YamlSequenceNode sequence + ? sequence.Children[index] + : throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {index}. Only Sequences can be indexed by number."); + } + } + + /// + /// Gets the value associated with a key in a . + /// + public YamlNode this[YamlNode key] + { + get + { + return this is YamlMappingNode mapping + ? mapping.Children[key] + : throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {key}. Only Mappings can be indexed by key."); + } + } + } +} diff --git a/YamlDotNet/RepresentationModel/YamlNodeIdentityEqualityComparer.cs b/YamlDotNet/RepresentationModel/YamlNodeIdentityEqualityComparer.cs index 383fe85f5..47744c806 100644 --- a/YamlDotNet/RepresentationModel/YamlNodeIdentityEqualityComparer.cs +++ b/YamlDotNet/RepresentationModel/YamlNodeIdentityEqualityComparer.cs @@ -1,48 +1,48 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Comparer that is based on identity comparisons. - /// - public sealed class YamlNodeIdentityEqualityComparer : IEqualityComparer - { - #region IEqualityComparer Members - - /// - public bool Equals([AllowNull] YamlNode x, [AllowNull] YamlNode y) - { - return ReferenceEquals(x, y); - } - - /// - public int GetHashCode(YamlNode obj) - { - return obj.GetHashCode(); - } - - #endregion - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Comparer that is based on identity comparisons. + /// + public sealed class YamlNodeIdentityEqualityComparer : IEqualityComparer + { + #region IEqualityComparer Members + + /// + public bool Equals([AllowNull] YamlNode x, [AllowNull] YamlNode y) + { + return ReferenceEquals(x, y); + } + + /// + public int GetHashCode(YamlNode obj) + { + return obj.GetHashCode(); + } + + #endregion + } +} diff --git a/YamlDotNet/RepresentationModel/YamlNodeType.cs b/YamlDotNet/RepresentationModel/YamlNodeType.cs index e6d0e27f4..70d78a724 100644 --- a/YamlDotNet/RepresentationModel/YamlNodeType.cs +++ b/YamlDotNet/RepresentationModel/YamlNodeType.cs @@ -1,49 +1,49 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Specifies the type of node in the representation model. - /// - public enum YamlNodeType - { - /// - /// The node is a . - /// - Alias, - - /// - /// The node is a . - /// - Mapping, - - /// - /// The node is a . - /// - Scalar, - - /// - /// The node is a . - /// - Sequence - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Specifies the type of node in the representation model. + /// + public enum YamlNodeType + { + /// + /// The node is a . + /// + Alias, + + /// + /// The node is a . + /// + Mapping, + + /// + /// The node is a . + /// + Scalar, + + /// + /// The node is a . + /// + Sequence + } +} diff --git a/YamlDotNet/RepresentationModel/YamlScalarNode.cs b/YamlDotNet/RepresentationModel/YamlScalarNode.cs index f18c53124..ccd4a637a 100644 --- a/YamlDotNet/RepresentationModel/YamlScalarNode.cs +++ b/YamlDotNet/RepresentationModel/YamlScalarNode.cs @@ -1,180 +1,180 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; -using static YamlDotNet.Core.HashCode; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Represents a scalar node in the YAML document. - /// - [DebuggerDisplay("{Value}")] - public sealed class YamlScalarNode : YamlNode, IYamlConvertible - { - /// - /// Gets or sets the value of the node. - /// - /// The value. - public string? Value { get; set; } - - /// - /// Gets or sets the style of the node. - /// - /// The style. - public ScalarStyle Style { get; set; } - - /// - /// Initializes a new instance of the class. - /// - internal YamlScalarNode(IParser parser, DocumentLoadingState state) - { - Load(parser, state); - } - - private void Load(IParser parser, DocumentLoadingState state) - { - var scalar = parser.Consume(); - Load(scalar, state); - Value = scalar.Value; - Style = scalar.Style; - } - - /// - /// Initializes a new instance of the class. - /// - public YamlScalarNode() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The value. - public YamlScalarNode(string? value) - { - this.Value = value; - } - - /// - /// Resolves the aliases that could not be resolved when the node was created. - /// - /// The state of the document. - internal override void ResolveAliases(DocumentLoadingState state) - { - throw new NotSupportedException("Resolving an alias on a scalar node does not make sense"); - } - - /// - /// Saves the current node to the specified emitter. - /// - /// The emitter where the node is to be saved. - /// The state. - internal override void Emit(IEmitter emitter, EmitterState state) - { - emitter.Emit(new Scalar(Anchor, Tag, Value ?? string.Empty, Style, Tag.IsEmpty, false)); - } - - /// - /// Accepts the specified visitor by calling the appropriate Visit method on it. - /// - /// - /// A . - /// - public override void Accept(IYamlVisitor visitor) - { - visitor.Visit(this); - } - - /// - public override bool Equals(object? obj) - { - return obj is YamlScalarNode other - && Equals(Tag, other.Tag) - && Equals(Value, other.Value); - } - - /// - /// Serves as a hash function for a particular type. - /// - /// - /// A hash code for the current . - /// - public override int GetHashCode() - { - return CombineHashCodes(Tag.GetHashCode(), Value); - } - - /// - /// Performs an explicit conversion from to . - /// - /// The value. - /// The result of the conversion. - public static explicit operator string?(YamlScalarNode value) - { - return value.Value; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - internal override string ToString(RecursionLevel level) - { - return Value ?? string.Empty; - } - - /// - /// Recursively enumerates all the nodes from the document, starting on the current node, - /// and throwing - /// if is reached. - /// - internal override IEnumerable SafeAllNodes(RecursionLevel level) - { - yield return this; - } - - /// - /// Gets the type of node. - /// - public override YamlNodeType NodeType - { - get { return YamlNodeType.Scalar; } - } - - void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) - { - Load(parser, new DocumentLoadingState()); - } - - void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) - { - Emit(emitter, new EmitterState()); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; +using static YamlDotNet.Core.HashCode; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Represents a scalar node in the YAML document. + /// + [DebuggerDisplay("{Value}")] + public sealed class YamlScalarNode : YamlNode, IYamlConvertible + { + /// + /// Gets or sets the value of the node. + /// + /// The value. + public string? Value { get; set; } + + /// + /// Gets or sets the style of the node. + /// + /// The style. + public ScalarStyle Style { get; set; } + + /// + /// Initializes a new instance of the class. + /// + internal YamlScalarNode(IParser parser, DocumentLoadingState state) + { + Load(parser, state); + } + + private void Load(IParser parser, DocumentLoadingState state) + { + var scalar = parser.Consume(); + Load(scalar, state); + Value = scalar.Value; + Style = scalar.Style; + } + + /// + /// Initializes a new instance of the class. + /// + public YamlScalarNode() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public YamlScalarNode(string? value) + { + this.Value = value; + } + + /// + /// Resolves the aliases that could not be resolved when the node was created. + /// + /// The state of the document. + internal override void ResolveAliases(DocumentLoadingState state) + { + throw new NotSupportedException("Resolving an alias on a scalar node does not make sense"); + } + + /// + /// Saves the current node to the specified emitter. + /// + /// The emitter where the node is to be saved. + /// The state. + internal override void Emit(IEmitter emitter, EmitterState state) + { + emitter.Emit(new Scalar(Anchor, Tag, Value ?? string.Empty, Style, Tag.IsEmpty, false)); + } + + /// + /// Accepts the specified visitor by calling the appropriate Visit method on it. + /// + /// + /// A . + /// + public override void Accept(IYamlVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override bool Equals(object? obj) + { + return obj is YamlScalarNode other + && Equals(Tag, other.Tag) + && Equals(Value, other.Value); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return CombineHashCodes(Tag.GetHashCode(), Value); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator string?(YamlScalarNode value) + { + return value.Value; + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + internal override string ToString(RecursionLevel level) + { + return Value ?? string.Empty; + } + + /// + /// Recursively enumerates all the nodes from the document, starting on the current node, + /// and throwing + /// if is reached. + /// + internal override IEnumerable SafeAllNodes(RecursionLevel level) + { + yield return this; + } + + /// + /// Gets the type of node. + /// + public override YamlNodeType NodeType + { + get { return YamlNodeType.Scalar; } + } + + void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) + { + Load(parser, new DocumentLoadingState()); + } + + void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) + { + Emit(emitter, new EmitterState()); + } + } +} diff --git a/YamlDotNet/RepresentationModel/YamlSequenceNode.cs b/YamlDotNet/RepresentationModel/YamlSequenceNode.cs index e369c50c8..bbaf50c7f 100644 --- a/YamlDotNet/RepresentationModel/YamlSequenceNode.cs +++ b/YamlDotNet/RepresentationModel/YamlSequenceNode.cs @@ -1,303 +1,303 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Helpers; -using YamlDotNet.Serialization; -using static YamlDotNet.Core.HashCode; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Represents a sequence node in the YAML document. - /// - [DebuggerDisplay("Count = {children.Count}")] - public sealed class YamlSequenceNode : YamlNode, IEnumerable, IYamlConvertible - { - private readonly IList children = new List(); - - /// - /// Gets the collection of child nodes. - /// - /// The children. - public IList Children - { - get - { - return children; - } - } - - /// - /// Gets or sets the style of the node. - /// - /// The style. - public SequenceStyle Style { get; set; } - - - /// - /// Initializes a new instance of the class. - /// - internal YamlSequenceNode(IParser parser, DocumentLoadingState state) - { - Load(parser, state); - } - - private void Load(IParser parser, DocumentLoadingState state) - { - var sequence = parser.Consume(); - Load(sequence, state); - Style = sequence.Style; - - var hasUnresolvedAliases = false; - while (!parser.TryConsume(out var _)) - { - var child = ParseNode(parser, state); - children.Add(child); - hasUnresolvedAliases |= child is YamlAliasNode; - } - - if (hasUnresolvedAliases) - { - state.AddNodeWithUnresolvedAliases(this); - } - } - - /// - /// Initializes a new instance of the class. - /// - public YamlSequenceNode() - { - } - - /// - /// Initializes a new instance of the class. - /// - public YamlSequenceNode(params YamlNode[] children) - : this((IEnumerable)children) - { - } - - /// - /// Initializes a new instance of the class. - /// - public YamlSequenceNode(IEnumerable children) - { - foreach (var child in children) - { - this.children.Add(child); - } - } - - /// - /// Adds the specified child to the collection. - /// - /// The child. - public void Add(YamlNode child) - { - children.Add(child); - } - - /// - /// Adds a scalar node to the collection. - /// - /// The child. - public void Add(string child) - { - children.Add(new YamlScalarNode(child)); - } - - /// - /// Resolves the aliases that could not be resolved when the node was created. - /// - /// The state of the document. - internal override void ResolveAliases(DocumentLoadingState state) - { - for (var i = 0; i < children.Count; ++i) - { - if (children[i] is YamlAliasNode) - { - children[i] = state.GetNode(children[i].Anchor!, children[i].Start, children[i].End); - } - } - } - - /// - /// Saves the current node to the specified emitter. - /// - /// The emitter where the node is to be saved. - /// The state. - internal override void Emit(IEmitter emitter, EmitterState state) - { - emitter.Emit(new SequenceStart(Anchor, Tag, Tag.IsEmpty, Style)); - foreach (var node in children) - { - node.Save(emitter, state); - } - emitter.Emit(new SequenceEnd()); - } - - /// - /// Accepts the specified visitor by calling the appropriate Visit method on it. - /// - /// - /// A . - /// - public override void Accept(IYamlVisitor visitor) - { - visitor.Visit(this); - } - - /// - public override bool Equals(object? obj) - { - var other = obj as YamlSequenceNode; - var areEqual = other != null - && Equals(Tag, other.Tag) - && children.Count == other.children.Count; - - if (!areEqual) - { - return false; - } - - for (var i = 0; i < children.Count; ++i) - { - if (!Equals(children[i], other!.children[i])) - { - return false; - } - } - - return true; - } - - /// - /// Serves as a hash function for a particular type. - /// - /// - /// A hash code for the current . - /// - public override int GetHashCode() - { - var hashCode = 0; - foreach (var item in children) - { - hashCode = CombineHashCodes(hashCode, item); - } - hashCode = CombineHashCodes(hashCode, Tag); - return hashCode; - } - - /// - /// Recursively enumerates all the nodes from the document, starting on the current node, - /// and throwing - /// if is reached. - /// - internal override IEnumerable SafeAllNodes(RecursionLevel level) - { - level.Increment(); - yield return this; - foreach (var child in children) - { - foreach (var node in child.SafeAllNodes(level)) - { - yield return node; - } - } - level.Decrement(); - } - - /// - /// Gets the type of node. - /// - public override YamlNodeType NodeType - { - get { return YamlNodeType.Sequence; } - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - internal override string ToString(RecursionLevel level) - { - if (!level.TryIncrement()) - { - return MaximumRecursionLevelReachedToStringValue; - } - - using var textBuilder = StringBuilderPool.Rent(); - var text = textBuilder.Builder; - text.Append("[ "); - - foreach (var child in children) - { - if (text.Length > 2) - { - text.Append(", "); - } - text.Append(child.ToString(level)); - } - - text.Append(" ]"); - - level.Decrement(); - - return text.ToString(); - } - - #region IEnumerable Members - - /// - public IEnumerator GetEnumerator() - { - return Children.GetEnumerator(); - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) - { - Load(parser, new DocumentLoadingState()); - } - - void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) - { - Emit(emitter, new EmitterState()); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Helpers; +using YamlDotNet.Serialization; +using static YamlDotNet.Core.HashCode; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Represents a sequence node in the YAML document. + /// + [DebuggerDisplay("Count = {children.Count}")] + public sealed class YamlSequenceNode : YamlNode, IEnumerable, IYamlConvertible + { + private readonly IList children = new List(); + + /// + /// Gets the collection of child nodes. + /// + /// The children. + public IList Children + { + get + { + return children; + } + } + + /// + /// Gets or sets the style of the node. + /// + /// The style. + public SequenceStyle Style { get; set; } + + + /// + /// Initializes a new instance of the class. + /// + internal YamlSequenceNode(IParser parser, DocumentLoadingState state) + { + Load(parser, state); + } + + private void Load(IParser parser, DocumentLoadingState state) + { + var sequence = parser.Consume(); + Load(sequence, state); + Style = sequence.Style; + + var hasUnresolvedAliases = false; + while (!parser.TryConsume(out var _)) + { + var child = ParseNode(parser, state); + children.Add(child); + hasUnresolvedAliases |= child is YamlAliasNode; + } + + if (hasUnresolvedAliases) + { + state.AddNodeWithUnresolvedAliases(this); + } + } + + /// + /// Initializes a new instance of the class. + /// + public YamlSequenceNode() + { + } + + /// + /// Initializes a new instance of the class. + /// + public YamlSequenceNode(params YamlNode[] children) + : this((IEnumerable)children) + { + } + + /// + /// Initializes a new instance of the class. + /// + public YamlSequenceNode(IEnumerable children) + { + foreach (var child in children) + { + this.children.Add(child); + } + } + + /// + /// Adds the specified child to the collection. + /// + /// The child. + public void Add(YamlNode child) + { + children.Add(child); + } + + /// + /// Adds a scalar node to the collection. + /// + /// The child. + public void Add(string child) + { + children.Add(new YamlScalarNode(child)); + } + + /// + /// Resolves the aliases that could not be resolved when the node was created. + /// + /// The state of the document. + internal override void ResolveAliases(DocumentLoadingState state) + { + for (var i = 0; i < children.Count; ++i) + { + if (children[i] is YamlAliasNode) + { + children[i] = state.GetNode(children[i].Anchor!, children[i].Start, children[i].End); + } + } + } + + /// + /// Saves the current node to the specified emitter. + /// + /// The emitter where the node is to be saved. + /// The state. + internal override void Emit(IEmitter emitter, EmitterState state) + { + emitter.Emit(new SequenceStart(Anchor, Tag, Tag.IsEmpty, Style)); + foreach (var node in children) + { + node.Save(emitter, state); + } + emitter.Emit(new SequenceEnd()); + } + + /// + /// Accepts the specified visitor by calling the appropriate Visit method on it. + /// + /// + /// A . + /// + public override void Accept(IYamlVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override bool Equals(object? obj) + { + var other = obj as YamlSequenceNode; + var areEqual = other != null + && Equals(Tag, other.Tag) + && children.Count == other.children.Count; + + if (!areEqual) + { + return false; + } + + for (var i = 0; i < children.Count; ++i) + { + if (!Equals(children[i], other!.children[i])) + { + return false; + } + } + + return true; + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + var hashCode = 0; + foreach (var item in children) + { + hashCode = CombineHashCodes(hashCode, item); + } + hashCode = CombineHashCodes(hashCode, Tag); + return hashCode; + } + + /// + /// Recursively enumerates all the nodes from the document, starting on the current node, + /// and throwing + /// if is reached. + /// + internal override IEnumerable SafeAllNodes(RecursionLevel level) + { + level.Increment(); + yield return this; + foreach (var child in children) + { + foreach (var node in child.SafeAllNodes(level)) + { + yield return node; + } + } + level.Decrement(); + } + + /// + /// Gets the type of node. + /// + public override YamlNodeType NodeType + { + get { return YamlNodeType.Sequence; } + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + internal override string ToString(RecursionLevel level) + { + if (!level.TryIncrement()) + { + return MaximumRecursionLevelReachedToStringValue; + } + + using var textBuilder = StringBuilderPool.Rent(); + var text = textBuilder.Builder; + text.Append("[ "); + + foreach (var child in children) + { + if (text.Length > 2) + { + text.Append(", "); + } + text.Append(child.ToString(level)); + } + + text.Append(" ]"); + + level.Decrement(); + + return text.ToString(); + } + + #region IEnumerable Members + + /// + public IEnumerator GetEnumerator() + { + return Children.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) + { + Load(parser, new DocumentLoadingState()); + } + + void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) + { + Emit(emitter, new EmitterState()); + } + } +} diff --git a/YamlDotNet/RepresentationModel/YamlStream.cs b/YamlDotNet/RepresentationModel/YamlStream.cs index aee72f58f..9a744243e 100644 --- a/YamlDotNet/RepresentationModel/YamlStream.cs +++ b/YamlDotNet/RepresentationModel/YamlStream.cs @@ -1,172 +1,172 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using System.IO; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Represents an YAML stream. - /// - public class YamlStream : IEnumerable - { - private readonly IList documents = new List(); - - /// - /// Gets the documents inside the stream. - /// - /// The documents. - public IList Documents - { - get - { - return documents; - } - } - - /// - /// Initializes a new instance of the class. - /// - public YamlStream() - { - } - - /// - /// Initializes a new instance of the class. - /// - public YamlStream(params YamlDocument[] documents) - : this((IEnumerable)documents) - { - } - - /// - /// Initializes a new instance of the class. - /// - public YamlStream(IEnumerable documents) - { - foreach (var document in documents) - { - this.documents.Add(document); - } - } - - /// - /// Adds the specified document to the collection. - /// - /// The document. - public void Add(YamlDocument document) - { - documents.Add(document); - } - - /// - /// Loads the stream from the specified input. - /// - /// The input. - public void Load(TextReader input) - { - Load(new Parser(input)); - } - - /// - /// Loads the stream from the specified . - /// - public void Load(IParser parser) - { - documents.Clear(); - parser.Consume(); - while (!parser.TryConsume(out var _)) - { - var document = new YamlDocument(parser); - documents.Add(document); - } - } - - /// - /// Saves the stream to the specified output. - /// - /// The output. - public void Save(TextWriter output) - { - Save(output, true); - } - - /// - /// Saves the stream to the specified output. - /// - /// The output. - /// Indicates whether or not to assign node anchors. - public void Save(TextWriter output, bool assignAnchors) - { - Save(new Emitter(output), assignAnchors); - } - - /// - /// Saves the stream to the specified emitter. - /// - /// The emitter. - /// Indicates whether or not to assign node anchors. - public void Save(IEmitter emitter, bool assignAnchors) - { - emitter.Emit(new StreamStart()); - - foreach (var document in documents) - { - document.Save(emitter, assignAnchors); - } - - emitter.Emit(new StreamEnd()); - } - - /// - /// Accepts the specified visitor by calling the appropriate Visit method on it. - /// - /// - /// A . - /// - public void Accept(IYamlVisitor visitor) - { - visitor.Visit(this); - } - - #region IEnumerable Members - - /// - public IEnumerator GetEnumerator() - { - return documents.GetEnumerator(); - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using System.IO; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Represents an YAML stream. + /// + public class YamlStream : IEnumerable + { + private readonly IList documents = new List(); + + /// + /// Gets the documents inside the stream. + /// + /// The documents. + public IList Documents + { + get + { + return documents; + } + } + + /// + /// Initializes a new instance of the class. + /// + public YamlStream() + { + } + + /// + /// Initializes a new instance of the class. + /// + public YamlStream(params YamlDocument[] documents) + : this((IEnumerable)documents) + { + } + + /// + /// Initializes a new instance of the class. + /// + public YamlStream(IEnumerable documents) + { + foreach (var document in documents) + { + this.documents.Add(document); + } + } + + /// + /// Adds the specified document to the collection. + /// + /// The document. + public void Add(YamlDocument document) + { + documents.Add(document); + } + + /// + /// Loads the stream from the specified input. + /// + /// The input. + public void Load(TextReader input) + { + Load(new Parser(input)); + } + + /// + /// Loads the stream from the specified . + /// + public void Load(IParser parser) + { + documents.Clear(); + parser.Consume(); + while (!parser.TryConsume(out var _)) + { + var document = new YamlDocument(parser); + documents.Add(document); + } + } + + /// + /// Saves the stream to the specified output. + /// + /// The output. + public void Save(TextWriter output) + { + Save(output, true); + } + + /// + /// Saves the stream to the specified output. + /// + /// The output. + /// Indicates whether or not to assign node anchors. + public void Save(TextWriter output, bool assignAnchors) + { + Save(new Emitter(output), assignAnchors); + } + + /// + /// Saves the stream to the specified emitter. + /// + /// The emitter. + /// Indicates whether or not to assign node anchors. + public void Save(IEmitter emitter, bool assignAnchors) + { + emitter.Emit(new StreamStart()); + + foreach (var document in documents) + { + document.Save(emitter, assignAnchors); + } + + emitter.Emit(new StreamEnd()); + } + + /// + /// Accepts the specified visitor by calling the appropriate Visit method on it. + /// + /// + /// A . + /// + public void Accept(IYamlVisitor visitor) + { + visitor.Visit(this); + } + + #region IEnumerable Members + + /// + public IEnumerator GetEnumerator() + { + return documents.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} diff --git a/YamlDotNet/RepresentationModel/YamlVisitor.cs b/YamlDotNet/RepresentationModel/YamlVisitor.cs index dcaaeabed..0514fa5ae 100644 --- a/YamlDotNet/RepresentationModel/YamlVisitor.cs +++ b/YamlDotNet/RepresentationModel/YamlVisitor.cs @@ -1,233 +1,233 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Abstract implementation of that knows how to walk a complete Yaml object model. - /// - [Obsolete("Use YamlVisitorBase")] - public abstract class YamlVisitor : IYamlVisitor - { - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - protected virtual void Visit(YamlStream stream) - { - // Do nothing. - } - - /// - /// Called after this object finishes visiting a . - /// - /// - /// The that has been visited. - /// - protected virtual void Visited(YamlStream stream) - { - // Do nothing. - } - - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - protected virtual void Visit(YamlDocument document) - { - // Do nothing. - } - - /// - /// Called after this object finishes visiting a . - /// - /// - /// The that has been visited. - /// - protected virtual void Visited(YamlDocument document) - { - // Do nothing. - } - - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - protected virtual void Visit(YamlScalarNode scalar) - { - // Do nothing. - } - - /// - /// Called after this object finishes visiting a . - /// - /// - /// The that has been visited. - /// - protected virtual void Visited(YamlScalarNode scalar) - { - // Do nothing. - } - - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - protected virtual void Visit(YamlSequenceNode sequence) - { - // Do nothing. - } - - /// - /// Called after this object finishes visiting a . - /// - /// - /// The that has been visited. - /// - protected virtual void Visited(YamlSequenceNode sequence) - { - // Do nothing. - } - - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - protected virtual void Visit(YamlMappingNode mapping) - { - // Do nothing. - } - - /// - /// Called after this object finishes visiting a . - /// - /// - /// The that has been visited. - /// - protected virtual void Visited(YamlMappingNode mapping) - { - // Do nothing. - } - - /// - /// Visits every child of a . - /// - /// - /// The that is being visited. - /// - protected virtual void VisitChildren(YamlStream stream) - { - foreach (var document in stream.Documents) - { - document.Accept(this); - } - } - - /// - /// Visits every child of a . - /// - /// - /// The that is being visited. - /// - protected virtual void VisitChildren(YamlDocument document) - { - if (document.RootNode != null) - { - document.RootNode.Accept(this); - } - } - - /// - /// Visits every child of a . - /// - /// - /// The that is being visited. - /// - protected virtual void VisitChildren(YamlSequenceNode sequence) - { - foreach (var node in sequence.Children) - { - node.Accept(this); - } - } - - /// - /// Visits every child of a . - /// - /// - /// The that is being visited. - /// - protected virtual void VisitChildren(YamlMappingNode mapping) - { - foreach (var pair in mapping.Children) - { - pair.Key.Accept(this); - pair.Value.Accept(this); - } - } - - void IYamlVisitor.Visit(YamlStream stream) - { - Visit(stream); - VisitChildren(stream); - Visited(stream); - } - - void IYamlVisitor.Visit(YamlDocument document) - { - Visit(document); - VisitChildren(document); - Visited(document); - } - - void IYamlVisitor.Visit(YamlScalarNode scalar) - { - Visit(scalar); - Visited(scalar); - } - - void IYamlVisitor.Visit(YamlSequenceNode sequence) - { - Visit(sequence); - VisitChildren(sequence); - Visited(sequence); - } - - void IYamlVisitor.Visit(YamlMappingNode mapping) - { - Visit(mapping); - VisitChildren(mapping); - Visited(mapping); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Abstract implementation of that knows how to walk a complete Yaml object model. + /// + [Obsolete("Use YamlVisitorBase")] + public abstract class YamlVisitor : IYamlVisitor + { + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + protected virtual void Visit(YamlStream stream) + { + // Do nothing. + } + + /// + /// Called after this object finishes visiting a . + /// + /// + /// The that has been visited. + /// + protected virtual void Visited(YamlStream stream) + { + // Do nothing. + } + + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + protected virtual void Visit(YamlDocument document) + { + // Do nothing. + } + + /// + /// Called after this object finishes visiting a . + /// + /// + /// The that has been visited. + /// + protected virtual void Visited(YamlDocument document) + { + // Do nothing. + } + + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + protected virtual void Visit(YamlScalarNode scalar) + { + // Do nothing. + } + + /// + /// Called after this object finishes visiting a . + /// + /// + /// The that has been visited. + /// + protected virtual void Visited(YamlScalarNode scalar) + { + // Do nothing. + } + + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + protected virtual void Visit(YamlSequenceNode sequence) + { + // Do nothing. + } + + /// + /// Called after this object finishes visiting a . + /// + /// + /// The that has been visited. + /// + protected virtual void Visited(YamlSequenceNode sequence) + { + // Do nothing. + } + + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + protected virtual void Visit(YamlMappingNode mapping) + { + // Do nothing. + } + + /// + /// Called after this object finishes visiting a . + /// + /// + /// The that has been visited. + /// + protected virtual void Visited(YamlMappingNode mapping) + { + // Do nothing. + } + + /// + /// Visits every child of a . + /// + /// + /// The that is being visited. + /// + protected virtual void VisitChildren(YamlStream stream) + { + foreach (var document in stream.Documents) + { + document.Accept(this); + } + } + + /// + /// Visits every child of a . + /// + /// + /// The that is being visited. + /// + protected virtual void VisitChildren(YamlDocument document) + { + if (document.RootNode != null) + { + document.RootNode.Accept(this); + } + } + + /// + /// Visits every child of a . + /// + /// + /// The that is being visited. + /// + protected virtual void VisitChildren(YamlSequenceNode sequence) + { + foreach (var node in sequence.Children) + { + node.Accept(this); + } + } + + /// + /// Visits every child of a . + /// + /// + /// The that is being visited. + /// + protected virtual void VisitChildren(YamlMappingNode mapping) + { + foreach (var pair in mapping.Children) + { + pair.Key.Accept(this); + pair.Value.Accept(this); + } + } + + void IYamlVisitor.Visit(YamlStream stream) + { + Visit(stream); + VisitChildren(stream); + Visited(stream); + } + + void IYamlVisitor.Visit(YamlDocument document) + { + Visit(document); + VisitChildren(document); + Visited(document); + } + + void IYamlVisitor.Visit(YamlScalarNode scalar) + { + Visit(scalar); + Visited(scalar); + } + + void IYamlVisitor.Visit(YamlSequenceNode sequence) + { + Visit(sequence); + VisitChildren(sequence); + Visited(sequence); + } + + void IYamlVisitor.Visit(YamlMappingNode mapping) + { + Visit(mapping); + VisitChildren(mapping); + Visited(mapping); + } + } +} diff --git a/YamlDotNet/RepresentationModel/YamlVisitorBase.cs b/YamlDotNet/RepresentationModel/YamlVisitorBase.cs index 5f2d2096e..088526e0f 100644 --- a/YamlDotNet/RepresentationModel/YamlVisitorBase.cs +++ b/YamlDotNet/RepresentationModel/YamlVisitorBase.cs @@ -1,151 +1,151 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.RepresentationModel -{ - /// - /// Abstract implementation of that knows how to walk a complete YAML object model. - /// - public abstract class YamlVisitorBase : IYamlVisitor - { - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - public virtual void Visit(YamlStream stream) - { - VisitChildren(stream); - } - - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - public virtual void Visit(YamlDocument document) - { - VisitChildren(document); - } - - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - public virtual void Visit(YamlScalarNode scalar) - { - // Do nothing. - } - - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - public virtual void Visit(YamlSequenceNode sequence) - { - VisitChildren(sequence); - } - - /// - /// Called when this object is visiting a . - /// - /// - /// The that is being visited. - /// - public virtual void Visit(YamlMappingNode mapping) - { - VisitChildren(mapping); - } - - /// - /// Called when this object is visiting a key-value pair. - /// - /// The left (key) that is being visited. - /// The right (value) that is being visited. - protected virtual void VisitPair(YamlNode key, YamlNode value) - { - key.Accept(this); - value.Accept(this); - } - - /// - /// Visits every child of a . - /// - /// - /// The that is being visited. - /// - protected virtual void VisitChildren(YamlStream stream) - { - foreach (var document in stream.Documents) - { - document.Accept(this); - } - } - - /// - /// Visits every child of a . - /// - /// - /// The that is being visited. - /// - protected virtual void VisitChildren(YamlDocument document) - { - if (document.RootNode != null) - { - document.RootNode.Accept(this); - } - } - - /// - /// Visits every child of a . - /// - /// - /// The that is being visited. - /// - protected virtual void VisitChildren(YamlSequenceNode sequence) - { - foreach (var node in sequence.Children) - { - node.Accept(this); - } - } - - /// - /// Visits every child of a . - /// - /// - /// The that is being visited. - /// - protected virtual void VisitChildren(YamlMappingNode mapping) - { - foreach (var pair in mapping.Children) - { - VisitPair(pair.Key, pair.Value); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.RepresentationModel +{ + /// + /// Abstract implementation of that knows how to walk a complete YAML object model. + /// + public abstract class YamlVisitorBase : IYamlVisitor + { + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + public virtual void Visit(YamlStream stream) + { + VisitChildren(stream); + } + + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + public virtual void Visit(YamlDocument document) + { + VisitChildren(document); + } + + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + public virtual void Visit(YamlScalarNode scalar) + { + // Do nothing. + } + + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + public virtual void Visit(YamlSequenceNode sequence) + { + VisitChildren(sequence); + } + + /// + /// Called when this object is visiting a . + /// + /// + /// The that is being visited. + /// + public virtual void Visit(YamlMappingNode mapping) + { + VisitChildren(mapping); + } + + /// + /// Called when this object is visiting a key-value pair. + /// + /// The left (key) that is being visited. + /// The right (value) that is being visited. + protected virtual void VisitPair(YamlNode key, YamlNode value) + { + key.Accept(this); + value.Accept(this); + } + + /// + /// Visits every child of a . + /// + /// + /// The that is being visited. + /// + protected virtual void VisitChildren(YamlStream stream) + { + foreach (var document in stream.Documents) + { + document.Accept(this); + } + } + + /// + /// Visits every child of a . + /// + /// + /// The that is being visited. + /// + protected virtual void VisitChildren(YamlDocument document) + { + if (document.RootNode != null) + { + document.RootNode.Accept(this); + } + } + + /// + /// Visits every child of a . + /// + /// + /// The that is being visited. + /// + protected virtual void VisitChildren(YamlSequenceNode sequence) + { + foreach (var node in sequence.Children) + { + node.Accept(this); + } + } + + /// + /// Visits every child of a . + /// + /// + /// The that is being visited. + /// + protected virtual void VisitChildren(YamlMappingNode mapping) + { + foreach (var pair in mapping.Children) + { + VisitPair(pair.Key, pair.Value); + } + } + } +} diff --git a/YamlDotNet/Serialization/BuilderSkeleton.cs b/YamlDotNet/Serialization/BuilderSkeleton.cs index b4d68f917..66b2336d2 100755 --- a/YamlDotNet/Serialization/BuilderSkeleton.cs +++ b/YamlDotNet/Serialization/BuilderSkeleton.cs @@ -1,326 +1,326 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Serialization.Converters; -using YamlDotNet.Serialization.NamingConventions; -using YamlDotNet.Serialization.TypeInspectors; - -namespace YamlDotNet.Serialization -{ - /// - /// Common implementation of and . - /// - public abstract class BuilderSkeleton - where TBuilder : BuilderSkeleton - { - internal INamingConvention namingConvention = NullNamingConvention.Instance; - internal ITypeResolver typeResolver; - internal readonly YamlAttributeOverrides overrides; - internal readonly LazyComponentRegistrationList typeConverterFactories; - internal readonly LazyComponentRegistrationList typeInspectorFactories; - internal bool ignoreFields; - internal bool includeNonPublicProperties = false; - - internal BuilderSkeleton(ITypeResolver typeResolver) - { - overrides = new YamlAttributeOverrides(); - - typeConverterFactories = new LazyComponentRegistrationList - { - { typeof(GuidConverter), _ => new GuidConverter(false) }, - { typeof(SystemTypeConverter), _ => new SystemTypeConverter() } - }; - - typeInspectorFactories = new LazyComponentRegistrationList(); - this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); - } - - protected abstract TBuilder Self { get; } - - internal virtual ITypeInspector BuildTypeInspector() - { - ITypeInspector innerInspector = new ReadablePropertiesTypeInspector(typeResolver, includeNonPublicProperties); - if (!ignoreFields) - { - innerInspector = new CompositeTypeInspector( - new ReadableFieldsTypeInspector(typeResolver), - innerInspector - ); - } - - return typeInspectorFactories.BuildComponentChain(innerInspector); - } - - /// - /// Prevents serialization and deserialization of fields. - /// - /// - public TBuilder IgnoreFields() - { - ignoreFields = true; - return Self; - } - - /// - /// Allows serialization and deserialization of non-public properties. - /// - public TBuilder IncludeNonPublicProperties() - { - includeNonPublicProperties = true; - return Self; - } - - /// - /// Sets the that will be used by the (de)serializer. - /// - public TBuilder WithNamingConvention(INamingConvention namingConvention) - { - this.namingConvention = namingConvention ?? throw new ArgumentNullException(nameof(namingConvention)); - return Self; - } - - /// - /// Sets the that will be used by the (de)serializer. - /// - public TBuilder WithTypeResolver(ITypeResolver typeResolver) - { - this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); - return Self; - } - - public abstract TBuilder WithTagMapping(TagName tag, Type type); - -#if !NET20 - /// - /// Register an for a given property. - /// - /// - /// An expression in the form: x => x.SomeProperty - /// The attribute to register. - /// - public TBuilder WithAttributeOverride(System.Linq.Expressions.Expression> propertyAccessor, Attribute attribute) - { - overrides.Add(propertyAccessor, attribute); - return Self; - } -#endif - - /// - /// Register an for a given property. - /// - public TBuilder WithAttributeOverride(Type type, string member, Attribute attribute) - { - overrides.Add(type, member, attribute); - return Self; - } - - /// - /// Registers an additional to be used by the (de)serializer. - /// - public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter) - { - return WithTypeConverter(typeConverter, w => w.OnTop()); - } - - /// - /// Registers an additional to be used by the (de)serializer. - /// - /// - /// Configures the location where to insert the - public TBuilder WithTypeConverter( - IYamlTypeConverter typeConverter, - Action> where - ) - { - if (typeConverter == null) - { - throw new ArgumentNullException(nameof(typeConverter)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(typeConverterFactories.CreateRegistrationLocationSelector(typeConverter.GetType(), _ => typeConverter)); - return Self; - } - - /// - /// Registers an additional to be used by the (de)serializer. - /// - /// A factory that creates the based on a previously registered . - /// Configures the location where to insert the - public TBuilder WithTypeConverter( - WrapperFactory typeConverterFactory, - Action> where - ) - where TYamlTypeConverter : IYamlTypeConverter - { - if (typeConverterFactory == null) - { - throw new ArgumentNullException(nameof(typeConverterFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(typeConverterFactories.CreateTrackingRegistrationLocationSelector(typeof(TYamlTypeConverter), (wrapped, _) => typeConverterFactory(wrapped))); - return Self; - } - - /// - /// Unregisters an existing of type . - /// - public TBuilder WithoutTypeConverter() - where TYamlTypeConverter : IYamlTypeConverter - { - return WithoutTypeConverter(typeof(TYamlTypeConverter)); - } - - /// - /// Unregisters an existing of type . - /// - public TBuilder WithoutTypeConverter(Type converterType) - { - if (converterType == null) - { - throw new ArgumentNullException(nameof(converterType)); - } - - typeConverterFactories.Remove(converterType); - return Self; - } - - /// - /// Registers an additional to be used by the (de)serializer. - /// - /// A function that instantiates the type inspector. - public TBuilder WithTypeInspector(Func typeInspectorFactory) - where TTypeInspector : ITypeInspector - { - return WithTypeInspector(typeInspectorFactory, w => w.OnTop()); - } - - /// - /// Registers an additional to be used by the (de)serializer. - /// - /// A function that instantiates the type inspector. - /// Configures the location where to insert the - public TBuilder WithTypeInspector( - Func typeInspectorFactory, - Action> where - ) - where TTypeInspector : ITypeInspector - { - if (typeInspectorFactory == null) - { - throw new ArgumentNullException(nameof(typeInspectorFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(typeInspectorFactories.CreateRegistrationLocationSelector(typeof(TTypeInspector), inner => typeInspectorFactory(inner))); - return Self; - } - - /// - /// Registers an additional to be used by the (de)serializer. - /// - /// A function that instantiates the type inspector based on a previously registered .. - /// Configures the location where to insert the - public TBuilder WithTypeInspector( - WrapperFactory typeInspectorFactory, - Action> where - ) - where TTypeInspector : ITypeInspector - { - if (typeInspectorFactory == null) - { - throw new ArgumentNullException(nameof(typeInspectorFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(typeInspectorFactories.CreateTrackingRegistrationLocationSelector(typeof(TTypeInspector), (wrapped, inner) => typeInspectorFactory(wrapped, inner))); - return Self; - } - - /// - /// Unregisters an existing of type . - /// - public TBuilder WithoutTypeInspector() - where TTypeInspector : ITypeInspector - { - return WithoutTypeInspector(typeof(TTypeInspector)); - } - - /// - /// Unregisters an existing of type . - /// - public TBuilder WithoutTypeInspector(Type inspectorType) - { - if (inspectorType == null) - { - throw new ArgumentNullException(nameof(inspectorType)); - } - - typeInspectorFactories.Remove(inspectorType); - return Self; - } - - protected IEnumerable BuildTypeConverters() - { - return typeConverterFactories.BuildComponentList(); - } - } - - /// - /// A factory that creates instances of based on an existing . - /// - /// The type of the wrapped component. - /// The type of the component that this factory creates. - /// The component that is to be wrapped. - /// Returns a new instance of that is based on . - public delegate TComponent WrapperFactory(TComponentBase wrapped) where TComponent : TComponentBase; - - /// - /// A factory that creates instances of based on an existing and an argument. - /// - /// The type of the argument. - /// The type of the wrapped component. - /// The type of the component that this factory creates. - /// The component that is to be wrapped. - /// The argument of the factory. - /// Returns a new instance of that is based on and . - public delegate TComponent WrapperFactory(TComponentBase wrapped, TArgument argument) where TComponent : TComponentBase; -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Serialization.Converters; +using YamlDotNet.Serialization.NamingConventions; +using YamlDotNet.Serialization.TypeInspectors; + +namespace YamlDotNet.Serialization +{ + /// + /// Common implementation of and . + /// + public abstract class BuilderSkeleton + where TBuilder : BuilderSkeleton + { + internal INamingConvention namingConvention = NullNamingConvention.Instance; + internal ITypeResolver typeResolver; + internal readonly YamlAttributeOverrides overrides; + internal readonly LazyComponentRegistrationList typeConverterFactories; + internal readonly LazyComponentRegistrationList typeInspectorFactories; + internal bool ignoreFields; + internal bool includeNonPublicProperties = false; + + internal BuilderSkeleton(ITypeResolver typeResolver) + { + overrides = new YamlAttributeOverrides(); + + typeConverterFactories = new LazyComponentRegistrationList + { + { typeof(GuidConverter), _ => new GuidConverter(false) }, + { typeof(SystemTypeConverter), _ => new SystemTypeConverter() } + }; + + typeInspectorFactories = new LazyComponentRegistrationList(); + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); + } + + protected abstract TBuilder Self { get; } + + internal virtual ITypeInspector BuildTypeInspector() + { + ITypeInspector innerInspector = new ReadablePropertiesTypeInspector(typeResolver, includeNonPublicProperties); + if (!ignoreFields) + { + innerInspector = new CompositeTypeInspector( + new ReadableFieldsTypeInspector(typeResolver), + innerInspector + ); + } + + return typeInspectorFactories.BuildComponentChain(innerInspector); + } + + /// + /// Prevents serialization and deserialization of fields. + /// + /// + public TBuilder IgnoreFields() + { + ignoreFields = true; + return Self; + } + + /// + /// Allows serialization and deserialization of non-public properties. + /// + public TBuilder IncludeNonPublicProperties() + { + includeNonPublicProperties = true; + return Self; + } + + /// + /// Sets the that will be used by the (de)serializer. + /// + public TBuilder WithNamingConvention(INamingConvention namingConvention) + { + this.namingConvention = namingConvention ?? throw new ArgumentNullException(nameof(namingConvention)); + return Self; + } + + /// + /// Sets the that will be used by the (de)serializer. + /// + public TBuilder WithTypeResolver(ITypeResolver typeResolver) + { + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); + return Self; + } + + public abstract TBuilder WithTagMapping(TagName tag, Type type); + +#if !NET20 + /// + /// Register an for a given property. + /// + /// + /// An expression in the form: x => x.SomeProperty + /// The attribute to register. + /// + public TBuilder WithAttributeOverride(System.Linq.Expressions.Expression> propertyAccessor, Attribute attribute) + { + overrides.Add(propertyAccessor, attribute); + return Self; + } +#endif + + /// + /// Register an for a given property. + /// + public TBuilder WithAttributeOverride(Type type, string member, Attribute attribute) + { + overrides.Add(type, member, attribute); + return Self; + } + + /// + /// Registers an additional to be used by the (de)serializer. + /// + public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter) + { + return WithTypeConverter(typeConverter, w => w.OnTop()); + } + + /// + /// Registers an additional to be used by the (de)serializer. + /// + /// + /// Configures the location where to insert the + public TBuilder WithTypeConverter( + IYamlTypeConverter typeConverter, + Action> where + ) + { + if (typeConverter == null) + { + throw new ArgumentNullException(nameof(typeConverter)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(typeConverterFactories.CreateRegistrationLocationSelector(typeConverter.GetType(), _ => typeConverter)); + return Self; + } + + /// + /// Registers an additional to be used by the (de)serializer. + /// + /// A factory that creates the based on a previously registered . + /// Configures the location where to insert the + public TBuilder WithTypeConverter( + WrapperFactory typeConverterFactory, + Action> where + ) + where TYamlTypeConverter : IYamlTypeConverter + { + if (typeConverterFactory == null) + { + throw new ArgumentNullException(nameof(typeConverterFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(typeConverterFactories.CreateTrackingRegistrationLocationSelector(typeof(TYamlTypeConverter), (wrapped, _) => typeConverterFactory(wrapped))); + return Self; + } + + /// + /// Unregisters an existing of type . + /// + public TBuilder WithoutTypeConverter() + where TYamlTypeConverter : IYamlTypeConverter + { + return WithoutTypeConverter(typeof(TYamlTypeConverter)); + } + + /// + /// Unregisters an existing of type . + /// + public TBuilder WithoutTypeConverter(Type converterType) + { + if (converterType == null) + { + throw new ArgumentNullException(nameof(converterType)); + } + + typeConverterFactories.Remove(converterType); + return Self; + } + + /// + /// Registers an additional to be used by the (de)serializer. + /// + /// A function that instantiates the type inspector. + public TBuilder WithTypeInspector(Func typeInspectorFactory) + where TTypeInspector : ITypeInspector + { + return WithTypeInspector(typeInspectorFactory, w => w.OnTop()); + } + + /// + /// Registers an additional to be used by the (de)serializer. + /// + /// A function that instantiates the type inspector. + /// Configures the location where to insert the + public TBuilder WithTypeInspector( + Func typeInspectorFactory, + Action> where + ) + where TTypeInspector : ITypeInspector + { + if (typeInspectorFactory == null) + { + throw new ArgumentNullException(nameof(typeInspectorFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(typeInspectorFactories.CreateRegistrationLocationSelector(typeof(TTypeInspector), inner => typeInspectorFactory(inner))); + return Self; + } + + /// + /// Registers an additional to be used by the (de)serializer. + /// + /// A function that instantiates the type inspector based on a previously registered .. + /// Configures the location where to insert the + public TBuilder WithTypeInspector( + WrapperFactory typeInspectorFactory, + Action> where + ) + where TTypeInspector : ITypeInspector + { + if (typeInspectorFactory == null) + { + throw new ArgumentNullException(nameof(typeInspectorFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(typeInspectorFactories.CreateTrackingRegistrationLocationSelector(typeof(TTypeInspector), (wrapped, inner) => typeInspectorFactory(wrapped, inner))); + return Self; + } + + /// + /// Unregisters an existing of type . + /// + public TBuilder WithoutTypeInspector() + where TTypeInspector : ITypeInspector + { + return WithoutTypeInspector(typeof(TTypeInspector)); + } + + /// + /// Unregisters an existing of type . + /// + public TBuilder WithoutTypeInspector(Type inspectorType) + { + if (inspectorType == null) + { + throw new ArgumentNullException(nameof(inspectorType)); + } + + typeInspectorFactories.Remove(inspectorType); + return Self; + } + + protected IEnumerable BuildTypeConverters() + { + return typeConverterFactories.BuildComponentList(); + } + } + + /// + /// A factory that creates instances of based on an existing . + /// + /// The type of the wrapped component. + /// The type of the component that this factory creates. + /// The component that is to be wrapped. + /// Returns a new instance of that is based on . + public delegate TComponent WrapperFactory(TComponentBase wrapped) where TComponent : TComponentBase; + + /// + /// A factory that creates instances of based on an existing and an argument. + /// + /// The type of the argument. + /// The type of the wrapped component. + /// The type of the component that this factory creates. + /// The component that is to be wrapped. + /// The argument of the factory. + /// Returns a new instance of that is based on and . + public delegate TComponent WrapperFactory(TComponentBase wrapped, TArgument argument) where TComponent : TComponentBase; +} diff --git a/YamlDotNet/Serialization/Converters/DateTimeConverter.cs b/YamlDotNet/Serialization/Converters/DateTimeConverter.cs index ec6526d1b..3de9b9d1c 100644 --- a/YamlDotNet/Serialization/Converters/DateTimeConverter.cs +++ b/YamlDotNet/Serialization/Converters/DateTimeConverter.cs @@ -1,115 +1,115 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Globalization; -using System.Linq; - -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.Converters -{ - /// - /// This represents the YAML converter entity for . - /// - public class DateTimeConverter : IYamlTypeConverter - { - private readonly DateTimeKind kind; - private readonly IFormatProvider provider; - private readonly string[] formats; - - /// - /// Initializes a new instance of the class. - /// - /// value. Default value is . is considered as . - /// instance. Default value is . - /// List of date/time formats for parsing. Default value is "G". - /// On deserializing, all formats in the list are used for conversion, while on serializing, the first format in the list is used. - public DateTimeConverter(DateTimeKind kind = DateTimeKind.Utc, IFormatProvider? provider = null, params string[] formats) - { - this.kind = kind == DateTimeKind.Unspecified ? DateTimeKind.Utc : kind; - this.provider = provider ?? CultureInfo.InvariantCulture; - this.formats = formats.DefaultIfEmpty("G").ToArray(); - } - - /// - /// Gets a value indicating whether the current converter supports converting the specified type. - /// - /// to check. - /// Returns True, if the current converter supports; otherwise returns False. - public bool Accepts(Type type) - { - return type == typeof(DateTime); - } - - /// - /// Reads an object's state from a YAML parser. - /// - /// instance. - /// to convert. - /// Returns the instance converted. - /// On deserializing, all formats in the list are used for conversion. - public object ReadYaml(IParser parser, Type type) - { - var value = parser.Consume().Value; - var style = this.kind == DateTimeKind.Local ? DateTimeStyles.AssumeLocal : DateTimeStyles.AssumeUniversal; - - var dt = DateTime.ParseExact(value, this.formats, this.provider, style); - dt = EnsureDateTimeKind(dt, this.kind); - return dt; - } - - /// - /// Writes the specified object's state to a YAML emitter. - /// - /// instance. - /// Value to write. - /// to convert. - /// On serializing, the first format in the list is used. - public void WriteYaml(IEmitter emitter, object? value, Type type) - { - var dt = (DateTime)value!; - var adjusted = this.kind == DateTimeKind.Local ? dt.ToLocalTime() : dt.ToUniversalTime(); - var formatted = adjusted.ToString(this.formats.First(), this.provider); // Always take the first format of the list. - - emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, ScalarStyle.Any, true, false)); - } - - private static DateTime EnsureDateTimeKind(DateTime dt, DateTimeKind kind) - { - DateTime ensured; - if (dt.Kind == DateTimeKind.Local && kind == DateTimeKind.Utc) - { - ensured = dt.ToUniversalTime(); - return ensured; - } - - if (dt.Kind == DateTimeKind.Utc && kind == DateTimeKind.Local) - { - ensured = dt.ToLocalTime(); - return ensured; - } - - return dt; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Globalization; +using System.Linq; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.Converters +{ + /// + /// This represents the YAML converter entity for . + /// + public class DateTimeConverter : IYamlTypeConverter + { + private readonly DateTimeKind kind; + private readonly IFormatProvider provider; + private readonly string[] formats; + + /// + /// Initializes a new instance of the class. + /// + /// value. Default value is . is considered as . + /// instance. Default value is . + /// List of date/time formats for parsing. Default value is "G". + /// On deserializing, all formats in the list are used for conversion, while on serializing, the first format in the list is used. + public DateTimeConverter(DateTimeKind kind = DateTimeKind.Utc, IFormatProvider? provider = null, params string[] formats) + { + this.kind = kind == DateTimeKind.Unspecified ? DateTimeKind.Utc : kind; + this.provider = provider ?? CultureInfo.InvariantCulture; + this.formats = formats.DefaultIfEmpty("G").ToArray(); + } + + /// + /// Gets a value indicating whether the current converter supports converting the specified type. + /// + /// to check. + /// Returns True, if the current converter supports; otherwise returns False. + public bool Accepts(Type type) + { + return type == typeof(DateTime); + } + + /// + /// Reads an object's state from a YAML parser. + /// + /// instance. + /// to convert. + /// Returns the instance converted. + /// On deserializing, all formats in the list are used for conversion. + public object ReadYaml(IParser parser, Type type) + { + var value = parser.Consume().Value; + var style = this.kind == DateTimeKind.Local ? DateTimeStyles.AssumeLocal : DateTimeStyles.AssumeUniversal; + + var dt = DateTime.ParseExact(value, this.formats, this.provider, style); + dt = EnsureDateTimeKind(dt, this.kind); + return dt; + } + + /// + /// Writes the specified object's state to a YAML emitter. + /// + /// instance. + /// Value to write. + /// to convert. + /// On serializing, the first format in the list is used. + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var dt = (DateTime)value!; + var adjusted = this.kind == DateTimeKind.Local ? dt.ToLocalTime() : dt.ToUniversalTime(); + var formatted = adjusted.ToString(this.formats.First(), this.provider); // Always take the first format of the list. + + emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, ScalarStyle.Any, true, false)); + } + + private static DateTime EnsureDateTimeKind(DateTime dt, DateTimeKind kind) + { + DateTime ensured; + if (dt.Kind == DateTimeKind.Local && kind == DateTimeKind.Utc) + { + ensured = dt.ToUniversalTime(); + return ensured; + } + + if (dt.Kind == DateTimeKind.Utc && kind == DateTimeKind.Local) + { + ensured = dt.ToLocalTime(); + return ensured; + } + + return dt; + } + } +} diff --git a/YamlDotNet/Serialization/Converters/GuidConverter.cs b/YamlDotNet/Serialization/Converters/GuidConverter.cs index cfe7d6540..e93707bf8 100644 --- a/YamlDotNet/Serialization/Converters/GuidConverter.cs +++ b/YamlDotNet/Serialization/Converters/GuidConverter.cs @@ -1,57 +1,57 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.Converters -{ - /// - /// Converter for System.Guid. - /// - public class GuidConverter : IYamlTypeConverter - { - private readonly bool jsonCompatible; - - public GuidConverter(bool jsonCompatible) - { - this.jsonCompatible = jsonCompatible; - } - - public bool Accepts(Type type) - { - return type == typeof(Guid); - } - - public object ReadYaml(IParser parser, Type type) - { - var value = parser.Consume().Value; - return new Guid(value); - } - - public void WriteYaml(IEmitter emitter, object? value, Type type) - { - var guid = (Guid)value!; - emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, guid.ToString("D"), jsonCompatible ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, true, false)); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.Converters +{ + /// + /// Converter for System.Guid. + /// + public class GuidConverter : IYamlTypeConverter + { + private readonly bool jsonCompatible; + + public GuidConverter(bool jsonCompatible) + { + this.jsonCompatible = jsonCompatible; + } + + public bool Accepts(Type type) + { + return type == typeof(Guid); + } + + public object ReadYaml(IParser parser, Type type) + { + var value = parser.Consume().Value; + return new Guid(value); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var guid = (Guid)value!; + emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, guid.ToString("D"), jsonCompatible ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, true, false)); + } + } +} diff --git a/YamlDotNet/Serialization/Converters/SystemTypeConverter.cs b/YamlDotNet/Serialization/Converters/SystemTypeConverter.cs index 4de2ee561..c819b0bae 100644 --- a/YamlDotNet/Serialization/Converters/SystemTypeConverter.cs +++ b/YamlDotNet/Serialization/Converters/SystemTypeConverter.cs @@ -1,53 +1,53 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.Converters -{ - /// - /// Converter for System.Type. - /// - /// - /// Converts to a scalar containing the assembly qualified name of the type. - /// - public class SystemTypeConverter : IYamlTypeConverter - { - public bool Accepts(Type type) - { - return typeof(Type).IsAssignableFrom(type); - } - - public object ReadYaml(IParser parser, Type type) - { - var value = parser.Consume().Value; - return Type.GetType(value, throwOnError: true)!; // Will throw instead of returning null - } - - public void WriteYaml(IEmitter emitter, object? value, Type type) - { - var systemType = (Type)value!; - emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, systemType.AssemblyQualifiedName!, ScalarStyle.Any, true, false)); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.Converters +{ + /// + /// Converter for System.Type. + /// + /// + /// Converts to a scalar containing the assembly qualified name of the type. + /// + public class SystemTypeConverter : IYamlTypeConverter + { + public bool Accepts(Type type) + { + return typeof(Type).IsAssignableFrom(type); + } + + public object ReadYaml(IParser parser, Type type) + { + var value = parser.Consume().Value; + return Type.GetType(value, throwOnError: true)!; // Will throw instead of returning null + } + + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var systemType = (Type)value!; + emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, systemType.AssemblyQualifiedName!, ScalarStyle.Any, true, false)); + } + } +} diff --git a/YamlDotNet/Serialization/DefaultValuesHandling.cs b/YamlDotNet/Serialization/DefaultValuesHandling.cs index 176a45bd0..f1ecab847 100644 --- a/YamlDotNet/Serialization/DefaultValuesHandling.cs +++ b/YamlDotNet/Serialization/DefaultValuesHandling.cs @@ -1,52 +1,52 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Serialization -{ - /// - /// Specifies the strategy to handle default and null values during serialization of properties. - /// - [Flags] - public enum DefaultValuesHandling - { - /// - /// Specifies that all properties are to be emitted regardless of their value. This is the default behavior. - /// - Preserve = 0, - - /// - /// Specifies that properties that contain null references or a null Nullable<T> are to be omitted. - /// - OmitNull = 1, - - /// - /// Specifies that properties that that contain their default value, either default(T) or the value specified in DefaultValueAttribute are to be omitted. - /// - OmitDefaults = 2, - - /// - /// Specifies that properties that that contain collections/arrays/enumerations that are empty are to be omitted. - /// - OmitEmptyCollections = 4, - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Serialization +{ + /// + /// Specifies the strategy to handle default and null values during serialization of properties. + /// + [Flags] + public enum DefaultValuesHandling + { + /// + /// Specifies that all properties are to be emitted regardless of their value. This is the default behavior. + /// + Preserve = 0, + + /// + /// Specifies that properties that contain null references or a null Nullable<T> are to be omitted. + /// + OmitNull = 1, + + /// + /// Specifies that properties that that contain their default value, either default(T) or the value specified in DefaultValueAttribute are to be omitted. + /// + OmitDefaults = 2, + + /// + /// Specifies that properties that that contain collections/arrays/enumerations that are empty are to be omitted. + /// + OmitEmptyCollections = 4, + } +} diff --git a/YamlDotNet/Serialization/Deserializer.cs b/YamlDotNet/Serialization/Deserializer.cs index 82acc4e3f..1c94d9c60 100644 --- a/YamlDotNet/Serialization/Deserializer.cs +++ b/YamlDotNet/Serialization/Deserializer.cs @@ -1,149 +1,149 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization -{ - /// - /// Deserializes objects from the YAML format. - /// To customize the behavior of , - /// use the class. - /// - public sealed class Deserializer : IDeserializer - { - private readonly IValueDeserializer valueDeserializer; - - /// - /// Initializes a new instance of using the default configuration. - /// - /// - /// To customize the behavior of the deserializer, use . - /// - public Deserializer() - : this(new DeserializerBuilder().BuildValueDeserializer()) - { - } - - /// - /// This constructor is private to discourage its use. - /// To invoke it, call the method. - /// - private Deserializer(IValueDeserializer valueDeserializer) - { - this.valueDeserializer = valueDeserializer ?? throw new ArgumentNullException(nameof(valueDeserializer)); - } - - /// - /// Creates a new that uses the specified . - /// This method is available for advanced scenarios. The preferred way to customize the behavior of the - /// deserializer is to use . - /// - public static Deserializer FromValueDeserializer(IValueDeserializer valueDeserializer) - { - return new Deserializer(valueDeserializer); - } - - public T Deserialize(string input) - { - using var reader = new StringReader(input); - return Deserialize(reader); - } - - public T Deserialize(TextReader input) - { - return Deserialize(new Parser(input)); - } - - public object? Deserialize(TextReader input) - { - return Deserialize(input, typeof(object)); - } - - public object? Deserialize(string input, Type type) - { - using var reader = new StringReader(input); - return Deserialize(reader, type); - } - - public object? Deserialize(TextReader input, Type type) - { - return Deserialize(new Parser(input), type); - } - - public T Deserialize(IParser parser) - { - return (T)Deserialize(parser, typeof(T))!; // We really want an exception if we are trying to deserialize null into a non-nullable type - } - - public object? Deserialize(IParser parser) - { - return Deserialize(parser, typeof(object)); - } - - /// - /// Deserializes an object of the specified type. - /// - /// The from where to deserialize the object. - /// The static type of the object to deserialize. - /// Returns the deserialized object. - public object? Deserialize(IParser parser, Type type) - { - if (parser == null) - { - throw new ArgumentNullException(nameof(parser)); - } - - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - var hasStreamStart = parser.TryConsume(out var _); - - var hasDocumentStart = parser.TryConsume(out var _); - - object? result = null; - if (!parser.Accept(out var _) && !parser.Accept(out var _)) - { - using var state = new SerializerState(); - result = valueDeserializer.DeserializeValue(parser, type, state, valueDeserializer); - state.OnDeserialization(); - } - - if (hasDocumentStart) - { - parser.Consume(); - } - - if (hasStreamStart) - { - parser.Consume(); - } - - return result; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.IO; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization +{ + /// + /// Deserializes objects from the YAML format. + /// To customize the behavior of , + /// use the class. + /// + public sealed class Deserializer : IDeserializer + { + private readonly IValueDeserializer valueDeserializer; + + /// + /// Initializes a new instance of using the default configuration. + /// + /// + /// To customize the behavior of the deserializer, use . + /// + public Deserializer() + : this(new DeserializerBuilder().BuildValueDeserializer()) + { + } + + /// + /// This constructor is private to discourage its use. + /// To invoke it, call the method. + /// + private Deserializer(IValueDeserializer valueDeserializer) + { + this.valueDeserializer = valueDeserializer ?? throw new ArgumentNullException(nameof(valueDeserializer)); + } + + /// + /// Creates a new that uses the specified . + /// This method is available for advanced scenarios. The preferred way to customize the behavior of the + /// deserializer is to use . + /// + public static Deserializer FromValueDeserializer(IValueDeserializer valueDeserializer) + { + return new Deserializer(valueDeserializer); + } + + public T Deserialize(string input) + { + using var reader = new StringReader(input); + return Deserialize(reader); + } + + public T Deserialize(TextReader input) + { + return Deserialize(new Parser(input)); + } + + public object? Deserialize(TextReader input) + { + return Deserialize(input, typeof(object)); + } + + public object? Deserialize(string input, Type type) + { + using var reader = new StringReader(input); + return Deserialize(reader, type); + } + + public object? Deserialize(TextReader input, Type type) + { + return Deserialize(new Parser(input), type); + } + + public T Deserialize(IParser parser) + { + return (T)Deserialize(parser, typeof(T))!; // We really want an exception if we are trying to deserialize null into a non-nullable type + } + + public object? Deserialize(IParser parser) + { + return Deserialize(parser, typeof(object)); + } + + /// + /// Deserializes an object of the specified type. + /// + /// The from where to deserialize the object. + /// The static type of the object to deserialize. + /// Returns the deserialized object. + public object? Deserialize(IParser parser, Type type) + { + if (parser == null) + { + throw new ArgumentNullException(nameof(parser)); + } + + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var hasStreamStart = parser.TryConsume(out var _); + + var hasDocumentStart = parser.TryConsume(out var _); + + object? result = null; + if (!parser.Accept(out var _) && !parser.Accept(out var _)) + { + using var state = new SerializerState(); + result = valueDeserializer.DeserializeValue(parser, type, state, valueDeserializer); + state.OnDeserialization(); + } + + if (hasDocumentStart) + { + parser.Consume(); + } + + if (hasStreamStart) + { + parser.Consume(); + } + + return result; + } + } +} diff --git a/YamlDotNet/Serialization/DeserializerBuilder.cs b/YamlDotNet/Serialization/DeserializerBuilder.cs index f2abd70ba..237eb3ca2 100755 --- a/YamlDotNet/Serialization/DeserializerBuilder.cs +++ b/YamlDotNet/Serialization/DeserializerBuilder.cs @@ -1,414 +1,414 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Serialization.NamingConventions; -using YamlDotNet.Serialization.NodeDeserializers; -using YamlDotNet.Serialization.NodeTypeResolvers; -using YamlDotNet.Serialization.ObjectFactories; -using YamlDotNet.Serialization.Schemas; -using YamlDotNet.Serialization.TypeInspectors; -using YamlDotNet.Serialization.TypeResolvers; -using YamlDotNet.Serialization.ValueDeserializers; - -namespace YamlDotNet.Serialization -{ - /// - /// Creates and configures instances of . - /// This class is used to customize the behavior of . Use the relevant methods - /// to apply customizations, then call to create an instance of the deserializer - /// with the desired customizations. - /// - public sealed class DeserializerBuilder : BuilderSkeleton - { - private Lazy objectFactory; - private readonly LazyComponentRegistrationList nodeDeserializerFactories; - private readonly LazyComponentRegistrationList nodeTypeResolverFactories; - private readonly Dictionary tagMappings; - private readonly Dictionary typeMappings; - private bool ignoreUnmatched; - private bool attemptUnknownTypeDeserialization; - - /// - /// Initializes a new using the default component registrations. - /// - public DeserializerBuilder() - : base(new StaticTypeResolver()) - { - typeMappings = new Dictionary(); - objectFactory = new Lazy(() => new DefaultObjectFactory(typeMappings), true); - - tagMappings = new Dictionary - { - { FailsafeSchema.Tags.Map, typeof(Dictionary) }, - { FailsafeSchema.Tags.Str, typeof(string) }, - { JsonSchema.Tags.Bool, typeof(bool) }, - { JsonSchema.Tags.Float, typeof(double) }, - { JsonSchema.Tags.Int, typeof(int) }, - { DefaultSchema.Tags.Timestamp, typeof(DateTime) } - }; - - typeInspectorFactories.Add(typeof(CachedTypeInspector), inner => new CachedTypeInspector(inner)); - typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), inner => namingConvention is NullNamingConvention ? inner : new NamingConventionTypeInspector(inner, namingConvention)); - typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), inner => new YamlAttributesTypeInspector(inner)); - typeInspectorFactories.Add(typeof(YamlAttributeOverridesInspector), inner => overrides != null ? new YamlAttributeOverridesInspector(inner, overrides.Clone()) : inner); - typeInspectorFactories.Add(typeof(ReadableAndWritablePropertiesTypeInspector), inner => new ReadableAndWritablePropertiesTypeInspector(inner)); - - nodeDeserializerFactories = new LazyComponentRegistrationList - { - { typeof(YamlConvertibleNodeDeserializer), _ => new YamlConvertibleNodeDeserializer(objectFactory.Value) }, - { typeof(YamlSerializableNodeDeserializer), _ => new YamlSerializableNodeDeserializer(objectFactory.Value) }, - { typeof(TypeConverterNodeDeserializer), _ => new TypeConverterNodeDeserializer(BuildTypeConverters()) }, - { typeof(NullNodeDeserializer), _ => new NullNodeDeserializer() }, - { typeof(ScalarNodeDeserializer), _ => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization) }, - { typeof(ArrayNodeDeserializer), _ => new ArrayNodeDeserializer() }, - { typeof(DictionaryNodeDeserializer), _ => new DictionaryNodeDeserializer(objectFactory.Value) }, - { typeof(CollectionNodeDeserializer), _ => new CollectionNodeDeserializer(objectFactory.Value) }, - { typeof(EnumerableNodeDeserializer), _ => new EnumerableNodeDeserializer() }, - { typeof(ObjectNodeDeserializer), _ => new ObjectNodeDeserializer(objectFactory.Value, BuildTypeInspector(), ignoreUnmatched) } - }; - - nodeTypeResolverFactories = new LazyComponentRegistrationList - { - { typeof(MappingNodeTypeResolver), _ => new MappingNodeTypeResolver(typeMappings) }, - { typeof(YamlConvertibleTypeResolver), _ => new YamlConvertibleTypeResolver() }, - { typeof(YamlSerializableTypeResolver), _ => new YamlSerializableTypeResolver() }, - { typeof(TagNodeTypeResolver), _ => new TagNodeTypeResolver(tagMappings) }, - { typeof(PreventUnknownTagsNodeTypeResolver), _ => new PreventUnknownTagsNodeTypeResolver() }, - { typeof(DefaultContainersNodeTypeResolver), _ => new DefaultContainersNodeTypeResolver() } - }; - } - - protected override DeserializerBuilder Self { get { return this; } } - - internal override ITypeInspector BuildTypeInspector() - { - ITypeInspector innerInspector = new WritablePropertiesTypeInspector(typeResolver, includeNonPublicProperties); - if (!ignoreFields) - { - innerInspector = new CompositeTypeInspector( - new ReadableFieldsTypeInspector(typeResolver), - innerInspector - ); - } - - return typeInspectorFactories.BuildComponentChain(innerInspector); - } - - /// - /// When deserializing it will attempt to convert unquoted strings to their correct datatype. If conversion is not sucessful, it will leave it as a string. - /// This option is only applicable when not specifying a type or specifying the object type during deserialization. - /// - public DeserializerBuilder WithAttemptingUnquotedStringTypeDeserialization() - { - attemptUnknownTypeDeserialization = true; - return this; - } - - /// - /// Sets the that will be used by the deserializer. - /// - public DeserializerBuilder WithObjectFactory(IObjectFactory objectFactory) - { - if (objectFactory == null) - { - throw new ArgumentNullException(nameof(objectFactory)); - } - - this.objectFactory = new Lazy(() => objectFactory, true); - return this; - } - - /// - /// Sets the that will be used by the deserializer. - /// - public DeserializerBuilder WithObjectFactory(Func objectFactory) - { - if (objectFactory == null) - { - throw new ArgumentNullException(nameof(objectFactory)); - } - - return WithObjectFactory(new LambdaObjectFactory(objectFactory)); - } - - /// - /// Registers an additional to be used by the deserializer. - /// - public DeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer) - { - return WithNodeDeserializer(nodeDeserializer, w => w.OnTop()); - } - - /// - /// Registers an additional to be used by the deserializer. - /// - /// - /// Configures the location where to insert the - public DeserializerBuilder WithNodeDeserializer( - INodeDeserializer nodeDeserializer, - Action> where - ) - { - if (nodeDeserializer == null) - { - throw new ArgumentNullException(nameof(nodeDeserializer)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(nodeDeserializerFactories.CreateRegistrationLocationSelector(nodeDeserializer.GetType(), _ => nodeDeserializer)); - return this; - } - - /// - /// Registers an additional to be used by the deserializer. - /// - /// A factory that creates the based on a previously registered . - /// Configures the location where to insert the - public DeserializerBuilder WithNodeDeserializer( - WrapperFactory nodeDeserializerFactory, - Action> where - ) - where TNodeDeserializer : INodeDeserializer - { - if (nodeDeserializerFactory == null) - { - throw new ArgumentNullException(nameof(nodeDeserializerFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(nodeDeserializerFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeDeserializer), (wrapped, _) => nodeDeserializerFactory(wrapped))); - return this; - } - - /// - /// Unregisters an existing of type . - /// - public DeserializerBuilder WithoutNodeDeserializer() - where TNodeDeserializer : INodeDeserializer - { - return WithoutNodeDeserializer(typeof(TNodeDeserializer)); - } - - /// - /// Unregisters an existing of type . - /// - public DeserializerBuilder WithoutNodeDeserializer(Type nodeDeserializerType) - { - if (nodeDeserializerType == null) - { - throw new ArgumentNullException(nameof(nodeDeserializerType)); - } - - nodeDeserializerFactories.Remove(nodeDeserializerType); - return this; - } - - /// - /// Registers an additional to be used by the deserializer. - /// - public DeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver) - { - return WithNodeTypeResolver(nodeTypeResolver, w => w.OnTop()); - } - - /// - /// Registers an additional to be used by the deserializer. - /// - /// - /// Configures the location where to insert the - public DeserializerBuilder WithNodeTypeResolver( - INodeTypeResolver nodeTypeResolver, - Action> where - ) - { - if (nodeTypeResolver == null) - { - throw new ArgumentNullException(nameof(nodeTypeResolver)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(nodeTypeResolverFactories.CreateRegistrationLocationSelector(nodeTypeResolver.GetType(), _ => nodeTypeResolver)); - return this; - } - - /// - /// Registers an additional to be used by the deserializer. - /// - /// A factory that creates the based on a previously registered . - /// Configures the location where to insert the - public DeserializerBuilder WithNodeTypeResolver( - WrapperFactory nodeTypeResolverFactory, - Action> where - ) - where TNodeTypeResolver : INodeTypeResolver - { - if (nodeTypeResolverFactory == null) - { - throw new ArgumentNullException(nameof(nodeTypeResolverFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(nodeTypeResolverFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeTypeResolver), (wrapped, _) => nodeTypeResolverFactory(wrapped))); - return this; - } - - /// - /// Unregisters an existing of type . - /// - public DeserializerBuilder WithoutNodeTypeResolver() - where TNodeTypeResolver : INodeTypeResolver - { - return WithoutNodeTypeResolver(typeof(TNodeTypeResolver)); - } - - /// - /// Unregisters an existing of type . - /// - public DeserializerBuilder WithoutNodeTypeResolver(Type nodeTypeResolverType) - { - if (nodeTypeResolverType == null) - { - throw new ArgumentNullException(nameof(nodeTypeResolverType)); - } - - nodeTypeResolverFactories.Remove(nodeTypeResolverType); - return this; - } - - /// - /// Registers a tag mapping. - /// - public override DeserializerBuilder WithTagMapping(TagName tag, Type type) - { - if (tag.IsEmpty) - { - throw new ArgumentException("Non-specific tags cannot be maped"); - } - - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - if (tagMappings.TryGetValue(tag, out var alreadyRegisteredType)) - { - throw new ArgumentException($"Type already has a registered type '{alreadyRegisteredType.FullName}' for tag '{tag}'", nameof(tag)); - } - - tagMappings.Add(tag, type); - return this; - } - - /// - /// Registers a type mapping using the default object factory. - /// - public DeserializerBuilder WithTypeMapping() - where TConcrete : TInterface - { - var interfaceType = typeof(TInterface); - var concreteType = typeof(TConcrete); - - if (!interfaceType.IsAssignableFrom(concreteType)) - { - throw new InvalidOperationException($"The type '{concreteType.Name}' does not implement interface '{interfaceType.Name}'."); - } - - if (typeMappings.ContainsKey(interfaceType)) - { - typeMappings[interfaceType] = concreteType; - } - else - { - typeMappings.Add(interfaceType, concreteType); - } - - return this; - } - - /// - /// Unregisters an existing tag mapping. - /// - public DeserializerBuilder WithoutTagMapping(TagName tag) - { - if (tag.IsEmpty) - { - throw new ArgumentException("Non-specific tags cannot be maped"); - } - - if (!tagMappings.Remove(tag)) - { - throw new KeyNotFoundException($"Tag '{tag}' is not registered"); - } - return this; - } - - /// - /// Instructs the deserializer to ignore unmatched properties instead of throwing an exception. - /// - public DeserializerBuilder IgnoreUnmatchedProperties() - { - ignoreUnmatched = true; - return this; - } - - /// - /// Creates a new according to the current configuration. - /// - public IDeserializer Build() - { - return Deserializer.FromValueDeserializer(BuildValueDeserializer()); - } - - /// - /// Creates a new that implements the current configuration. - /// This method is available for advanced scenarios. The preferred way to customize the behavior of the - /// deserializer is to use the method. - /// - public IValueDeserializer BuildValueDeserializer() - { - return new AliasValueDeserializer( - new NodeValueDeserializer( - nodeDeserializerFactories.BuildComponentList(), - nodeTypeResolverFactories.BuildComponentList() - ) - ); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Serialization.NamingConventions; +using YamlDotNet.Serialization.NodeDeserializers; +using YamlDotNet.Serialization.NodeTypeResolvers; +using YamlDotNet.Serialization.ObjectFactories; +using YamlDotNet.Serialization.Schemas; +using YamlDotNet.Serialization.TypeInspectors; +using YamlDotNet.Serialization.TypeResolvers; +using YamlDotNet.Serialization.ValueDeserializers; + +namespace YamlDotNet.Serialization +{ + /// + /// Creates and configures instances of . + /// This class is used to customize the behavior of . Use the relevant methods + /// to apply customizations, then call to create an instance of the deserializer + /// with the desired customizations. + /// + public sealed class DeserializerBuilder : BuilderSkeleton + { + private Lazy objectFactory; + private readonly LazyComponentRegistrationList nodeDeserializerFactories; + private readonly LazyComponentRegistrationList nodeTypeResolverFactories; + private readonly Dictionary tagMappings; + private readonly Dictionary typeMappings; + private bool ignoreUnmatched; + private bool attemptUnknownTypeDeserialization; + + /// + /// Initializes a new using the default component registrations. + /// + public DeserializerBuilder() + : base(new StaticTypeResolver()) + { + typeMappings = new Dictionary(); + objectFactory = new Lazy(() => new DefaultObjectFactory(typeMappings), true); + + tagMappings = new Dictionary + { + { FailsafeSchema.Tags.Map, typeof(Dictionary) }, + { FailsafeSchema.Tags.Str, typeof(string) }, + { JsonSchema.Tags.Bool, typeof(bool) }, + { JsonSchema.Tags.Float, typeof(double) }, + { JsonSchema.Tags.Int, typeof(int) }, + { DefaultSchema.Tags.Timestamp, typeof(DateTime) } + }; + + typeInspectorFactories.Add(typeof(CachedTypeInspector), inner => new CachedTypeInspector(inner)); + typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), inner => namingConvention is NullNamingConvention ? inner : new NamingConventionTypeInspector(inner, namingConvention)); + typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), inner => new YamlAttributesTypeInspector(inner)); + typeInspectorFactories.Add(typeof(YamlAttributeOverridesInspector), inner => overrides != null ? new YamlAttributeOverridesInspector(inner, overrides.Clone()) : inner); + typeInspectorFactories.Add(typeof(ReadableAndWritablePropertiesTypeInspector), inner => new ReadableAndWritablePropertiesTypeInspector(inner)); + + nodeDeserializerFactories = new LazyComponentRegistrationList + { + { typeof(YamlConvertibleNodeDeserializer), _ => new YamlConvertibleNodeDeserializer(objectFactory.Value) }, + { typeof(YamlSerializableNodeDeserializer), _ => new YamlSerializableNodeDeserializer(objectFactory.Value) }, + { typeof(TypeConverterNodeDeserializer), _ => new TypeConverterNodeDeserializer(BuildTypeConverters()) }, + { typeof(NullNodeDeserializer), _ => new NullNodeDeserializer() }, + { typeof(ScalarNodeDeserializer), _ => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization) }, + { typeof(ArrayNodeDeserializer), _ => new ArrayNodeDeserializer() }, + { typeof(DictionaryNodeDeserializer), _ => new DictionaryNodeDeserializer(objectFactory.Value) }, + { typeof(CollectionNodeDeserializer), _ => new CollectionNodeDeserializer(objectFactory.Value) }, + { typeof(EnumerableNodeDeserializer), _ => new EnumerableNodeDeserializer() }, + { typeof(ObjectNodeDeserializer), _ => new ObjectNodeDeserializer(objectFactory.Value, BuildTypeInspector(), ignoreUnmatched) } + }; + + nodeTypeResolverFactories = new LazyComponentRegistrationList + { + { typeof(MappingNodeTypeResolver), _ => new MappingNodeTypeResolver(typeMappings) }, + { typeof(YamlConvertibleTypeResolver), _ => new YamlConvertibleTypeResolver() }, + { typeof(YamlSerializableTypeResolver), _ => new YamlSerializableTypeResolver() }, + { typeof(TagNodeTypeResolver), _ => new TagNodeTypeResolver(tagMappings) }, + { typeof(PreventUnknownTagsNodeTypeResolver), _ => new PreventUnknownTagsNodeTypeResolver() }, + { typeof(DefaultContainersNodeTypeResolver), _ => new DefaultContainersNodeTypeResolver() } + }; + } + + protected override DeserializerBuilder Self { get { return this; } } + + internal override ITypeInspector BuildTypeInspector() + { + ITypeInspector innerInspector = new WritablePropertiesTypeInspector(typeResolver, includeNonPublicProperties); + if (!ignoreFields) + { + innerInspector = new CompositeTypeInspector( + new ReadableFieldsTypeInspector(typeResolver), + innerInspector + ); + } + + return typeInspectorFactories.BuildComponentChain(innerInspector); + } + + /// + /// When deserializing it will attempt to convert unquoted strings to their correct datatype. If conversion is not sucessful, it will leave it as a string. + /// This option is only applicable when not specifying a type or specifying the object type during deserialization. + /// + public DeserializerBuilder WithAttemptingUnquotedStringTypeDeserialization() + { + attemptUnknownTypeDeserialization = true; + return this; + } + + /// + /// Sets the that will be used by the deserializer. + /// + public DeserializerBuilder WithObjectFactory(IObjectFactory objectFactory) + { + if (objectFactory == null) + { + throw new ArgumentNullException(nameof(objectFactory)); + } + + this.objectFactory = new Lazy(() => objectFactory, true); + return this; + } + + /// + /// Sets the that will be used by the deserializer. + /// + public DeserializerBuilder WithObjectFactory(Func objectFactory) + { + if (objectFactory == null) + { + throw new ArgumentNullException(nameof(objectFactory)); + } + + return WithObjectFactory(new LambdaObjectFactory(objectFactory)); + } + + /// + /// Registers an additional to be used by the deserializer. + /// + public DeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer) + { + return WithNodeDeserializer(nodeDeserializer, w => w.OnTop()); + } + + /// + /// Registers an additional to be used by the deserializer. + /// + /// + /// Configures the location where to insert the + public DeserializerBuilder WithNodeDeserializer( + INodeDeserializer nodeDeserializer, + Action> where + ) + { + if (nodeDeserializer == null) + { + throw new ArgumentNullException(nameof(nodeDeserializer)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(nodeDeserializerFactories.CreateRegistrationLocationSelector(nodeDeserializer.GetType(), _ => nodeDeserializer)); + return this; + } + + /// + /// Registers an additional to be used by the deserializer. + /// + /// A factory that creates the based on a previously registered . + /// Configures the location where to insert the + public DeserializerBuilder WithNodeDeserializer( + WrapperFactory nodeDeserializerFactory, + Action> where + ) + where TNodeDeserializer : INodeDeserializer + { + if (nodeDeserializerFactory == null) + { + throw new ArgumentNullException(nameof(nodeDeserializerFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(nodeDeserializerFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeDeserializer), (wrapped, _) => nodeDeserializerFactory(wrapped))); + return this; + } + + /// + /// Unregisters an existing of type . + /// + public DeserializerBuilder WithoutNodeDeserializer() + where TNodeDeserializer : INodeDeserializer + { + return WithoutNodeDeserializer(typeof(TNodeDeserializer)); + } + + /// + /// Unregisters an existing of type . + /// + public DeserializerBuilder WithoutNodeDeserializer(Type nodeDeserializerType) + { + if (nodeDeserializerType == null) + { + throw new ArgumentNullException(nameof(nodeDeserializerType)); + } + + nodeDeserializerFactories.Remove(nodeDeserializerType); + return this; + } + + /// + /// Registers an additional to be used by the deserializer. + /// + public DeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver) + { + return WithNodeTypeResolver(nodeTypeResolver, w => w.OnTop()); + } + + /// + /// Registers an additional to be used by the deserializer. + /// + /// + /// Configures the location where to insert the + public DeserializerBuilder WithNodeTypeResolver( + INodeTypeResolver nodeTypeResolver, + Action> where + ) + { + if (nodeTypeResolver == null) + { + throw new ArgumentNullException(nameof(nodeTypeResolver)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(nodeTypeResolverFactories.CreateRegistrationLocationSelector(nodeTypeResolver.GetType(), _ => nodeTypeResolver)); + return this; + } + + /// + /// Registers an additional to be used by the deserializer. + /// + /// A factory that creates the based on a previously registered . + /// Configures the location where to insert the + public DeserializerBuilder WithNodeTypeResolver( + WrapperFactory nodeTypeResolverFactory, + Action> where + ) + where TNodeTypeResolver : INodeTypeResolver + { + if (nodeTypeResolverFactory == null) + { + throw new ArgumentNullException(nameof(nodeTypeResolverFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(nodeTypeResolverFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeTypeResolver), (wrapped, _) => nodeTypeResolverFactory(wrapped))); + return this; + } + + /// + /// Unregisters an existing of type . + /// + public DeserializerBuilder WithoutNodeTypeResolver() + where TNodeTypeResolver : INodeTypeResolver + { + return WithoutNodeTypeResolver(typeof(TNodeTypeResolver)); + } + + /// + /// Unregisters an existing of type . + /// + public DeserializerBuilder WithoutNodeTypeResolver(Type nodeTypeResolverType) + { + if (nodeTypeResolverType == null) + { + throw new ArgumentNullException(nameof(nodeTypeResolverType)); + } + + nodeTypeResolverFactories.Remove(nodeTypeResolverType); + return this; + } + + /// + /// Registers a tag mapping. + /// + public override DeserializerBuilder WithTagMapping(TagName tag, Type type) + { + if (tag.IsEmpty) + { + throw new ArgumentException("Non-specific tags cannot be maped"); + } + + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (tagMappings.TryGetValue(tag, out var alreadyRegisteredType)) + { + throw new ArgumentException($"Type already has a registered type '{alreadyRegisteredType.FullName}' for tag '{tag}'", nameof(tag)); + } + + tagMappings.Add(tag, type); + return this; + } + + /// + /// Registers a type mapping using the default object factory. + /// + public DeserializerBuilder WithTypeMapping() + where TConcrete : TInterface + { + var interfaceType = typeof(TInterface); + var concreteType = typeof(TConcrete); + + if (!interfaceType.IsAssignableFrom(concreteType)) + { + throw new InvalidOperationException($"The type '{concreteType.Name}' does not implement interface '{interfaceType.Name}'."); + } + + if (typeMappings.ContainsKey(interfaceType)) + { + typeMappings[interfaceType] = concreteType; + } + else + { + typeMappings.Add(interfaceType, concreteType); + } + + return this; + } + + /// + /// Unregisters an existing tag mapping. + /// + public DeserializerBuilder WithoutTagMapping(TagName tag) + { + if (tag.IsEmpty) + { + throw new ArgumentException("Non-specific tags cannot be maped"); + } + + if (!tagMappings.Remove(tag)) + { + throw new KeyNotFoundException($"Tag '{tag}' is not registered"); + } + return this; + } + + /// + /// Instructs the deserializer to ignore unmatched properties instead of throwing an exception. + /// + public DeserializerBuilder IgnoreUnmatchedProperties() + { + ignoreUnmatched = true; + return this; + } + + /// + /// Creates a new according to the current configuration. + /// + public IDeserializer Build() + { + return Deserializer.FromValueDeserializer(BuildValueDeserializer()); + } + + /// + /// Creates a new that implements the current configuration. + /// This method is available for advanced scenarios. The preferred way to customize the behavior of the + /// deserializer is to use the method. + /// + public IValueDeserializer BuildValueDeserializer() + { + return new AliasValueDeserializer( + new NodeValueDeserializer( + nodeDeserializerFactories.BuildComponentList(), + nodeTypeResolverFactories.BuildComponentList() + ) + ); + } + } +} diff --git a/YamlDotNet/Serialization/EmissionPhaseObjectGraphVisitorArgs.cs b/YamlDotNet/Serialization/EmissionPhaseObjectGraphVisitorArgs.cs index 320de8f7e..c2a5d1e85 100644 --- a/YamlDotNet/Serialization/EmissionPhaseObjectGraphVisitorArgs.cs +++ b/YamlDotNet/Serialization/EmissionPhaseObjectGraphVisitorArgs.cs @@ -1,82 +1,82 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public sealed class EmissionPhaseObjectGraphVisitorArgs - { - /// - /// Gets the next visitor that should be called by the current visitor. - /// - public IObjectGraphVisitor InnerVisitor { get; private set; } - - /// - /// Gets the that is to be used for serialization. - /// - public IEventEmitter EventEmitter { get; private set; } - - /// - /// Gets a function that, when called, serializes the specified object. - /// - public ObjectSerializer NestedObjectSerializer { get; private set; } - - public IEnumerable TypeConverters { get; private set; } - - private readonly IEnumerable> preProcessingPhaseVisitors; - - public EmissionPhaseObjectGraphVisitorArgs( - IObjectGraphVisitor innerVisitor, - IEventEmitter eventEmitter, - IEnumerable> preProcessingPhaseVisitors, - IEnumerable typeConverters, - ObjectSerializer nestedObjectSerializer - ) - { - InnerVisitor = innerVisitor ?? throw new ArgumentNullException(nameof(innerVisitor)); - EventEmitter = eventEmitter ?? throw new ArgumentNullException(nameof(eventEmitter)); - this.preProcessingPhaseVisitors = preProcessingPhaseVisitors ?? throw new ArgumentNullException(nameof(preProcessingPhaseVisitors)); - TypeConverters = typeConverters ?? throw new ArgumentNullException(nameof(typeConverters)); - NestedObjectSerializer = nestedObjectSerializer ?? throw new ArgumentNullException(nameof(nestedObjectSerializer)); - } - - /// - /// Gets the visitor of type that was used during the pre-processing phase. - /// - /// The type of the visitor.s - /// - /// - /// No visitor of that type has been registered, - /// or ore than one visitors registered are of type . - /// - public T GetPreProcessingPhaseObjectGraphVisitor() - where T : IObjectGraphVisitor - { - return preProcessingPhaseVisitors - .OfType() - .Single(); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public sealed class EmissionPhaseObjectGraphVisitorArgs + { + /// + /// Gets the next visitor that should be called by the current visitor. + /// + public IObjectGraphVisitor InnerVisitor { get; private set; } + + /// + /// Gets the that is to be used for serialization. + /// + public IEventEmitter EventEmitter { get; private set; } + + /// + /// Gets a function that, when called, serializes the specified object. + /// + public ObjectSerializer NestedObjectSerializer { get; private set; } + + public IEnumerable TypeConverters { get; private set; } + + private readonly IEnumerable> preProcessingPhaseVisitors; + + public EmissionPhaseObjectGraphVisitorArgs( + IObjectGraphVisitor innerVisitor, + IEventEmitter eventEmitter, + IEnumerable> preProcessingPhaseVisitors, + IEnumerable typeConverters, + ObjectSerializer nestedObjectSerializer + ) + { + InnerVisitor = innerVisitor ?? throw new ArgumentNullException(nameof(innerVisitor)); + EventEmitter = eventEmitter ?? throw new ArgumentNullException(nameof(eventEmitter)); + this.preProcessingPhaseVisitors = preProcessingPhaseVisitors ?? throw new ArgumentNullException(nameof(preProcessingPhaseVisitors)); + TypeConverters = typeConverters ?? throw new ArgumentNullException(nameof(typeConverters)); + NestedObjectSerializer = nestedObjectSerializer ?? throw new ArgumentNullException(nameof(nestedObjectSerializer)); + } + + /// + /// Gets the visitor of type that was used during the pre-processing phase. + /// + /// The type of the visitor.s + /// + /// + /// No visitor of that type has been registered, + /// or ore than one visitors registered are of type . + /// + public T GetPreProcessingPhaseObjectGraphVisitor() + where T : IObjectGraphVisitor + { + return preProcessingPhaseVisitors + .OfType() + .Single(); + } + } +} diff --git a/YamlDotNet/Serialization/EventEmitters/ChainedEventEmitter.cs b/YamlDotNet/Serialization/EventEmitters/ChainedEventEmitter.cs index 120837643..6410e5403 100644 --- a/YamlDotNet/Serialization/EventEmitters/ChainedEventEmitter.cs +++ b/YamlDotNet/Serialization/EventEmitters/ChainedEventEmitter.cs @@ -1,70 +1,70 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.EventEmitters -{ - /// - /// Provided the base implementation for an IEventEmitter that is a - /// decorator for another IEventEmitter. - /// - public abstract class ChainedEventEmitter : IEventEmitter - { - protected readonly IEventEmitter nextEmitter; - - protected ChainedEventEmitter(IEventEmitter nextEmitter) - { - this.nextEmitter = nextEmitter ?? throw new ArgumentNullException(nameof(nextEmitter)); - } - - public virtual void Emit(AliasEventInfo eventInfo, IEmitter emitter) - { - nextEmitter.Emit(eventInfo, emitter); - } - - public virtual void Emit(ScalarEventInfo eventInfo, IEmitter emitter) - { - nextEmitter.Emit(eventInfo, emitter); - } - - public virtual void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) - { - nextEmitter.Emit(eventInfo, emitter); - } - - public virtual void Emit(MappingEndEventInfo eventInfo, IEmitter emitter) - { - nextEmitter.Emit(eventInfo, emitter); - } - - public virtual void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) - { - nextEmitter.Emit(eventInfo, emitter); - } - - public virtual void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter) - { - nextEmitter.Emit(eventInfo, emitter); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.EventEmitters +{ + /// + /// Provided the base implementation for an IEventEmitter that is a + /// decorator for another IEventEmitter. + /// + public abstract class ChainedEventEmitter : IEventEmitter + { + protected readonly IEventEmitter nextEmitter; + + protected ChainedEventEmitter(IEventEmitter nextEmitter) + { + this.nextEmitter = nextEmitter ?? throw new ArgumentNullException(nameof(nextEmitter)); + } + + public virtual void Emit(AliasEventInfo eventInfo, IEmitter emitter) + { + nextEmitter.Emit(eventInfo, emitter); + } + + public virtual void Emit(ScalarEventInfo eventInfo, IEmitter emitter) + { + nextEmitter.Emit(eventInfo, emitter); + } + + public virtual void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) + { + nextEmitter.Emit(eventInfo, emitter); + } + + public virtual void Emit(MappingEndEventInfo eventInfo, IEmitter emitter) + { + nextEmitter.Emit(eventInfo, emitter); + } + + public virtual void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) + { + nextEmitter.Emit(eventInfo, emitter); + } + + public virtual void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter) + { + nextEmitter.Emit(eventInfo, emitter); + } + } +} diff --git a/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs b/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs index 3f10e442e..3d96a7286 100644 --- a/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs +++ b/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs @@ -1,126 +1,126 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.EventEmitters -{ - public sealed class JsonEventEmitter : ChainedEventEmitter - { - public JsonEventEmitter(IEventEmitter nextEmitter) - : base(nextEmitter) - { - } - - public override void Emit(AliasEventInfo eventInfo, IEmitter emitter) - { - eventInfo.NeedsExpansion = true; - } - - public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) - { - eventInfo.IsPlainImplicit = true; - eventInfo.Style = ScalarStyle.Plain; - - var value = eventInfo.Source.Value; - if (value == null) - { - eventInfo.RenderedValue = "null"; - } - else - { - var typeCode = eventInfo.Source.Type.GetTypeCode(); - switch (typeCode) - { - case TypeCode.Boolean: - eventInfo.RenderedValue = YamlFormatter.FormatBoolean(value); - break; - - case TypeCode.Byte: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - case TypeCode.SByte: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.UInt64: - var valueIsEnum = eventInfo.Source.Type.IsEnum(); - if (valueIsEnum) - { - eventInfo.RenderedValue = value.ToString()!; - eventInfo.Style = ScalarStyle.DoubleQuoted; - break; - } - - eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); - break; - - case TypeCode.Single: - case TypeCode.Double: - case TypeCode.Decimal: - eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); - break; - - case TypeCode.String: - case TypeCode.Char: - eventInfo.RenderedValue = value.ToString()!; - eventInfo.Style = ScalarStyle.DoubleQuoted; - break; - - case TypeCode.DateTime: - eventInfo.RenderedValue = YamlFormatter.FormatDateTime(value); - break; - - case TypeCode.Empty: - eventInfo.RenderedValue = "null"; - break; - - default: - if (eventInfo.Source.Type == typeof(TimeSpan)) - { - eventInfo.RenderedValue = YamlFormatter.FormatTimeSpan(value); - break; - } - - throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); - } - } - - base.Emit(eventInfo, emitter); - } - - public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) - { - eventInfo.Style = MappingStyle.Flow; - - base.Emit(eventInfo, emitter); - } - - public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) - { - eventInfo.Style = SequenceStyle.Flow; - - base.Emit(eventInfo, emitter); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.EventEmitters +{ + public sealed class JsonEventEmitter : ChainedEventEmitter + { + public JsonEventEmitter(IEventEmitter nextEmitter) + : base(nextEmitter) + { + } + + public override void Emit(AliasEventInfo eventInfo, IEmitter emitter) + { + eventInfo.NeedsExpansion = true; + } + + public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) + { + eventInfo.IsPlainImplicit = true; + eventInfo.Style = ScalarStyle.Plain; + + var value = eventInfo.Source.Value; + if (value == null) + { + eventInfo.RenderedValue = "null"; + } + else + { + var typeCode = eventInfo.Source.Type.GetTypeCode(); + switch (typeCode) + { + case TypeCode.Boolean: + eventInfo.RenderedValue = YamlFormatter.FormatBoolean(value); + break; + + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + var valueIsEnum = eventInfo.Source.Type.IsEnum(); + if (valueIsEnum) + { + eventInfo.RenderedValue = value.ToString()!; + eventInfo.Style = ScalarStyle.DoubleQuoted; + break; + } + + eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); + break; + + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); + break; + + case TypeCode.String: + case TypeCode.Char: + eventInfo.RenderedValue = value.ToString()!; + eventInfo.Style = ScalarStyle.DoubleQuoted; + break; + + case TypeCode.DateTime: + eventInfo.RenderedValue = YamlFormatter.FormatDateTime(value); + break; + + case TypeCode.Empty: + eventInfo.RenderedValue = "null"; + break; + + default: + if (eventInfo.Source.Type == typeof(TimeSpan)) + { + eventInfo.RenderedValue = YamlFormatter.FormatTimeSpan(value); + break; + } + + throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); + } + } + + base.Emit(eventInfo, emitter); + } + + public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) + { + eventInfo.Style = MappingStyle.Flow; + + base.Emit(eventInfo, emitter); + } + + public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) + { + eventInfo.Style = SequenceStyle.Flow; + + base.Emit(eventInfo, emitter); + } + } +} diff --git a/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs b/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs index fdef80d37..fa9dbfed2 100644 --- a/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs +++ b/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs @@ -1,194 +1,194 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using YamlDotNet.Core; -using YamlDotNet.Serialization.Schemas; - -namespace YamlDotNet.Serialization.EventEmitters -{ - public sealed class TypeAssigningEventEmitter : ChainedEventEmitter - { - private readonly bool requireTagWhenStaticAndActualTypesAreDifferent; - private readonly IDictionary tagMappings; - private readonly bool quoteNecessaryStrings; - private static readonly string IsSpecialStringValue_Regex = - @"^(" - + @"null|Null|NULL|\~" - + @"|true|True|TRUE|false|False|FALSE" - + @"|[-+]?[0-9]+|0o[0-7]+" - + @"|0x[0-9a-fA-F]+" - + @"|[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?" - + @"|[-+]?(\.inf|\.Inf|\.INF)" - + @"|\.nan|\.NaN|\.NAN" - + @")$"; - - public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings) - : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings) - { - this.quoteNecessaryStrings = quoteNecessaryStrings; - } - - public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings) - : base(nextEmitter) - { - this.requireTagWhenStaticAndActualTypesAreDifferent = requireTagWhenStaticAndActualTypesAreDifferent; - this.tagMappings = tagMappings ?? throw new ArgumentNullException(nameof(tagMappings)); - } - - public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) - { - var suggestedStyle = ScalarStyle.Plain; - - var value = eventInfo.Source.Value; - if (value == null) - { - eventInfo.Tag = JsonSchema.Tags.Null; - eventInfo.RenderedValue = ""; - } - else - { - var typeCode = eventInfo.Source.Type.GetTypeCode(); - switch (typeCode) - { - case TypeCode.Boolean: - eventInfo.Tag = JsonSchema.Tags.Bool; - eventInfo.RenderedValue = YamlFormatter.FormatBoolean(value); - break; - - case TypeCode.Byte: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - case TypeCode.SByte: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.UInt64: - eventInfo.Tag = JsonSchema.Tags.Int; - eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); - break; - - case TypeCode.Single: - eventInfo.Tag = JsonSchema.Tags.Float; - eventInfo.RenderedValue = YamlFormatter.FormatNumber((float)value); - break; - - case TypeCode.Double: - eventInfo.Tag = JsonSchema.Tags.Float; - eventInfo.RenderedValue = YamlFormatter.FormatNumber((double)value); - break; - - case TypeCode.Decimal: - eventInfo.Tag = JsonSchema.Tags.Float; - eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); - break; - - case TypeCode.String: - case TypeCode.Char: - eventInfo.Tag = FailsafeSchema.Tags.Str; - eventInfo.RenderedValue = value.ToString()!; - - if (quoteNecessaryStrings && IsSpecialStringValue(eventInfo.RenderedValue)) - { - suggestedStyle = ScalarStyle.DoubleQuoted; - } - else - { - suggestedStyle = ScalarStyle.Any; - } - - break; - - case TypeCode.DateTime: - eventInfo.Tag = DefaultSchema.Tags.Timestamp; - eventInfo.RenderedValue = YamlFormatter.FormatDateTime(value); - break; - - case TypeCode.Empty: - eventInfo.Tag = JsonSchema.Tags.Null; - eventInfo.RenderedValue = ""; - break; - - default: - if (eventInfo.Source.Type == typeof(TimeSpan)) - { - eventInfo.RenderedValue = YamlFormatter.FormatTimeSpan(value); - break; - } - - throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); - } - } - - eventInfo.IsPlainImplicit = true; - if (eventInfo.Style == ScalarStyle.Any) - { - eventInfo.Style = suggestedStyle; - } - - base.Emit(eventInfo, emitter); - } - - public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) - { - AssignTypeIfNeeded(eventInfo); - base.Emit(eventInfo, emitter); - } - - public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) - { - AssignTypeIfNeeded(eventInfo); - base.Emit(eventInfo, emitter); - } - - private void AssignTypeIfNeeded(ObjectEventInfo eventInfo) - { - if (tagMappings.TryGetValue(eventInfo.Source.Type, out var tag)) - { - eventInfo.Tag = tag; - } - else if (requireTagWhenStaticAndActualTypesAreDifferent && eventInfo.Source.Value != null && eventInfo.Source.Type != eventInfo.Source.StaticType) - { - throw new YamlException( - $"Cannot serialize type '{eventInfo.Source.Type.FullName}' where a '{eventInfo.Source.StaticType.FullName}' was expected " - + $"because no tag mapping has been registered for '{eventInfo.Source.Type.FullName}', " - + $"which means that it won't be possible to deserialize the document.\n" - + $"Register a tag mapping using the SerializerBuilder.WithTagMapping method.\n\n" - + $"E.g: builder.WithTagMapping(\"!{eventInfo.Source.Type.Name}\", typeof({eventInfo.Source.Type.FullName}));" - ); - } - } - - private bool IsSpecialStringValue(string value) - { - if (value.Trim() == string.Empty) - { - return true; - } - - return Regex.IsMatch( - value, - IsSpecialStringValue_Regex); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using YamlDotNet.Core; +using YamlDotNet.Serialization.Schemas; + +namespace YamlDotNet.Serialization.EventEmitters +{ + public sealed class TypeAssigningEventEmitter : ChainedEventEmitter + { + private readonly bool requireTagWhenStaticAndActualTypesAreDifferent; + private readonly IDictionary tagMappings; + private readonly bool quoteNecessaryStrings; + private static readonly string IsSpecialStringValue_Regex = + @"^(" + + @"null|Null|NULL|\~" + + @"|true|True|TRUE|false|False|FALSE" + + @"|[-+]?[0-9]+|0o[0-7]+" + + @"|0x[0-9a-fA-F]+" + + @"|[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?" + + @"|[-+]?(\.inf|\.Inf|\.INF)" + + @"|\.nan|\.NaN|\.NAN" + + @")$"; + + public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings) + : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings) + { + this.quoteNecessaryStrings = quoteNecessaryStrings; + } + + public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings) + : base(nextEmitter) + { + this.requireTagWhenStaticAndActualTypesAreDifferent = requireTagWhenStaticAndActualTypesAreDifferent; + this.tagMappings = tagMappings ?? throw new ArgumentNullException(nameof(tagMappings)); + } + + public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) + { + var suggestedStyle = ScalarStyle.Plain; + + var value = eventInfo.Source.Value; + if (value == null) + { + eventInfo.Tag = JsonSchema.Tags.Null; + eventInfo.RenderedValue = ""; + } + else + { + var typeCode = eventInfo.Source.Type.GetTypeCode(); + switch (typeCode) + { + case TypeCode.Boolean: + eventInfo.Tag = JsonSchema.Tags.Bool; + eventInfo.RenderedValue = YamlFormatter.FormatBoolean(value); + break; + + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + eventInfo.Tag = JsonSchema.Tags.Int; + eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); + break; + + case TypeCode.Single: + eventInfo.Tag = JsonSchema.Tags.Float; + eventInfo.RenderedValue = YamlFormatter.FormatNumber((float)value); + break; + + case TypeCode.Double: + eventInfo.Tag = JsonSchema.Tags.Float; + eventInfo.RenderedValue = YamlFormatter.FormatNumber((double)value); + break; + + case TypeCode.Decimal: + eventInfo.Tag = JsonSchema.Tags.Float; + eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); + break; + + case TypeCode.String: + case TypeCode.Char: + eventInfo.Tag = FailsafeSchema.Tags.Str; + eventInfo.RenderedValue = value.ToString()!; + + if (quoteNecessaryStrings && IsSpecialStringValue(eventInfo.RenderedValue)) + { + suggestedStyle = ScalarStyle.DoubleQuoted; + } + else + { + suggestedStyle = ScalarStyle.Any; + } + + break; + + case TypeCode.DateTime: + eventInfo.Tag = DefaultSchema.Tags.Timestamp; + eventInfo.RenderedValue = YamlFormatter.FormatDateTime(value); + break; + + case TypeCode.Empty: + eventInfo.Tag = JsonSchema.Tags.Null; + eventInfo.RenderedValue = ""; + break; + + default: + if (eventInfo.Source.Type == typeof(TimeSpan)) + { + eventInfo.RenderedValue = YamlFormatter.FormatTimeSpan(value); + break; + } + + throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); + } + } + + eventInfo.IsPlainImplicit = true; + if (eventInfo.Style == ScalarStyle.Any) + { + eventInfo.Style = suggestedStyle; + } + + base.Emit(eventInfo, emitter); + } + + public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) + { + AssignTypeIfNeeded(eventInfo); + base.Emit(eventInfo, emitter); + } + + public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) + { + AssignTypeIfNeeded(eventInfo); + base.Emit(eventInfo, emitter); + } + + private void AssignTypeIfNeeded(ObjectEventInfo eventInfo) + { + if (tagMappings.TryGetValue(eventInfo.Source.Type, out var tag)) + { + eventInfo.Tag = tag; + } + else if (requireTagWhenStaticAndActualTypesAreDifferent && eventInfo.Source.Value != null && eventInfo.Source.Type != eventInfo.Source.StaticType) + { + throw new YamlException( + $"Cannot serialize type '{eventInfo.Source.Type.FullName}' where a '{eventInfo.Source.StaticType.FullName}' was expected " + + $"because no tag mapping has been registered for '{eventInfo.Source.Type.FullName}', " + + $"which means that it won't be possible to deserialize the document.\n" + + $"Register a tag mapping using the SerializerBuilder.WithTagMapping method.\n\n" + + $"E.g: builder.WithTagMapping(\"!{eventInfo.Source.Type.Name}\", typeof({eventInfo.Source.Type.FullName}));" + ); + } + } + + private bool IsSpecialStringValue(string value) + { + if (value.Trim() == string.Empty) + { + return true; + } + + return Regex.IsMatch( + value, + IsSpecialStringValue_Regex); + } + } +} diff --git a/YamlDotNet/Serialization/EventEmitters/WriterEventEmitter.cs b/YamlDotNet/Serialization/EventEmitters/WriterEventEmitter.cs index 1f71b8e24..2ed2e2b52 100644 --- a/YamlDotNet/Serialization/EventEmitters/WriterEventEmitter.cs +++ b/YamlDotNet/Serialization/EventEmitters/WriterEventEmitter.cs @@ -1,59 +1,59 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.EventEmitters -{ - public sealed class WriterEventEmitter : IEventEmitter - { - void IEventEmitter.Emit(AliasEventInfo eventInfo, IEmitter emitter) - { - emitter.Emit(new AnchorAlias(eventInfo.Alias)); - } - - void IEventEmitter.Emit(ScalarEventInfo eventInfo, IEmitter emitter) - { - emitter.Emit(new Scalar(eventInfo.Anchor, eventInfo.Tag, eventInfo.RenderedValue, eventInfo.Style, eventInfo.IsPlainImplicit, eventInfo.IsQuotedImplicit)); - } - - void IEventEmitter.Emit(MappingStartEventInfo eventInfo, IEmitter emitter) - { - emitter.Emit(new MappingStart(eventInfo.Anchor, eventInfo.Tag, eventInfo.IsImplicit, eventInfo.Style)); - } - - void IEventEmitter.Emit(MappingEndEventInfo eventInfo, IEmitter emitter) - { - emitter.Emit(new MappingEnd()); - } - - void IEventEmitter.Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) - { - emitter.Emit(new SequenceStart(eventInfo.Anchor, eventInfo.Tag, eventInfo.IsImplicit, eventInfo.Style)); - } - - void IEventEmitter.Emit(SequenceEndEventInfo eventInfo, IEmitter emitter) - { - emitter.Emit(new SequenceEnd()); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.EventEmitters +{ + public sealed class WriterEventEmitter : IEventEmitter + { + void IEventEmitter.Emit(AliasEventInfo eventInfo, IEmitter emitter) + { + emitter.Emit(new AnchorAlias(eventInfo.Alias)); + } + + void IEventEmitter.Emit(ScalarEventInfo eventInfo, IEmitter emitter) + { + emitter.Emit(new Scalar(eventInfo.Anchor, eventInfo.Tag, eventInfo.RenderedValue, eventInfo.Style, eventInfo.IsPlainImplicit, eventInfo.IsQuotedImplicit)); + } + + void IEventEmitter.Emit(MappingStartEventInfo eventInfo, IEmitter emitter) + { + emitter.Emit(new MappingStart(eventInfo.Anchor, eventInfo.Tag, eventInfo.IsImplicit, eventInfo.Style)); + } + + void IEventEmitter.Emit(MappingEndEventInfo eventInfo, IEmitter emitter) + { + emitter.Emit(new MappingEnd()); + } + + void IEventEmitter.Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) + { + emitter.Emit(new SequenceStart(eventInfo.Anchor, eventInfo.Tag, eventInfo.IsImplicit, eventInfo.Style)); + } + + void IEventEmitter.Emit(SequenceEndEventInfo eventInfo, IEmitter emitter) + { + emitter.Emit(new SequenceEnd()); + } + } +} diff --git a/YamlDotNet/Serialization/EventInfo.cs b/YamlDotNet/Serialization/EventInfo.cs index 8455fc4b7..3dd49a333 100644 --- a/YamlDotNet/Serialization/EventInfo.cs +++ b/YamlDotNet/Serialization/EventInfo.cs @@ -1,117 +1,117 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization -{ - public abstract class EventInfo - { - public IObjectDescriptor Source { get; } - - protected EventInfo(IObjectDescriptor source) - { - Source = source ?? throw new ArgumentNullException(nameof(source)); - } - } - - public class AliasEventInfo : EventInfo - { - public AliasEventInfo(IObjectDescriptor source, AnchorName alias) - : base(source) - { - if (alias.IsEmpty) - { - throw new ArgumentNullException(nameof(alias)); - } - Alias = alias; - } - - public AnchorName Alias { get; } - public bool NeedsExpansion { get; set; } - } - - public class ObjectEventInfo : EventInfo - { - protected ObjectEventInfo(IObjectDescriptor source) - : base(source) - { - } - - public AnchorName Anchor { get; set; } - public TagName Tag { get; set; } - } - - public sealed class ScalarEventInfo : ObjectEventInfo - { - public ScalarEventInfo(IObjectDescriptor source) - : base(source) - { - Style = source.ScalarStyle; - RenderedValue = string.Empty; - } - - public string RenderedValue { get; set; } - public ScalarStyle Style { get; set; } - public bool IsPlainImplicit { get; set; } - public bool IsQuotedImplicit { get; set; } - } - - public sealed class MappingStartEventInfo : ObjectEventInfo - { - public MappingStartEventInfo(IObjectDescriptor source) - : base(source) - { - } - - public bool IsImplicit { get; set; } - public MappingStyle Style { get; set; } - } - - public sealed class MappingEndEventInfo : EventInfo - { - public MappingEndEventInfo(IObjectDescriptor source) - : base(source) - { - } - } - - public sealed class SequenceStartEventInfo : ObjectEventInfo - { - public SequenceStartEventInfo(IObjectDescriptor source) - : base(source) - { - } - - public bool IsImplicit { get; set; } - public SequenceStyle Style { get; set; } - } - - public sealed class SequenceEndEventInfo : EventInfo - { - public SequenceEndEventInfo(IObjectDescriptor source) - : base(source) - { - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization +{ + public abstract class EventInfo + { + public IObjectDescriptor Source { get; } + + protected EventInfo(IObjectDescriptor source) + { + Source = source ?? throw new ArgumentNullException(nameof(source)); + } + } + + public class AliasEventInfo : EventInfo + { + public AliasEventInfo(IObjectDescriptor source, AnchorName alias) + : base(source) + { + if (alias.IsEmpty) + { + throw new ArgumentNullException(nameof(alias)); + } + Alias = alias; + } + + public AnchorName Alias { get; } + public bool NeedsExpansion { get; set; } + } + + public class ObjectEventInfo : EventInfo + { + protected ObjectEventInfo(IObjectDescriptor source) + : base(source) + { + } + + public AnchorName Anchor { get; set; } + public TagName Tag { get; set; } + } + + public sealed class ScalarEventInfo : ObjectEventInfo + { + public ScalarEventInfo(IObjectDescriptor source) + : base(source) + { + Style = source.ScalarStyle; + RenderedValue = string.Empty; + } + + public string RenderedValue { get; set; } + public ScalarStyle Style { get; set; } + public bool IsPlainImplicit { get; set; } + public bool IsQuotedImplicit { get; set; } + } + + public sealed class MappingStartEventInfo : ObjectEventInfo + { + public MappingStartEventInfo(IObjectDescriptor source) + : base(source) + { + } + + public bool IsImplicit { get; set; } + public MappingStyle Style { get; set; } + } + + public sealed class MappingEndEventInfo : EventInfo + { + public MappingEndEventInfo(IObjectDescriptor source) + : base(source) + { + } + } + + public sealed class SequenceStartEventInfo : ObjectEventInfo + { + public SequenceStartEventInfo(IObjectDescriptor source) + : base(source) + { + } + + public bool IsImplicit { get; set; } + public SequenceStyle Style { get; set; } + } + + public sealed class SequenceEndEventInfo : EventInfo + { + public SequenceEndEventInfo(IObjectDescriptor source) + : base(source) + { + } + } +} diff --git a/YamlDotNet/Serialization/IAliasProvider.cs b/YamlDotNet/Serialization/IAliasProvider.cs index 3a84dc3de..2bba32120 100644 --- a/YamlDotNet/Serialization/IAliasProvider.cs +++ b/YamlDotNet/Serialization/IAliasProvider.cs @@ -1,30 +1,30 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public interface IAliasProvider - { - AnchorName GetAlias(object target); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public interface IAliasProvider + { + AnchorName GetAlias(object target); + } +} diff --git a/YamlDotNet/Serialization/IDeserializer.cs b/YamlDotNet/Serialization/IDeserializer.cs index b5bc3c919..bc6e97ea2 100644 --- a/YamlDotNet/Serialization/IDeserializer.cs +++ b/YamlDotNet/Serialization/IDeserializer.cs @@ -1,46 +1,46 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public interface IDeserializer - { - T Deserialize(string input); - T Deserialize(TextReader input); - object? Deserialize(TextReader input); - object? Deserialize(string input, Type type); - object? Deserialize(TextReader input, Type type); - T Deserialize(IParser parser); - object? Deserialize(IParser parser); - - /// - /// Deserializes an object of the specified type. - /// - /// The from where to deserialize the object. - /// The static type of the object to deserialize. - /// Returns the deserialized object. - object? Deserialize(IParser parser, Type type); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.IO; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public interface IDeserializer + { + T Deserialize(string input); + T Deserialize(TextReader input); + object? Deserialize(TextReader input); + object? Deserialize(string input, Type type); + object? Deserialize(TextReader input, Type type); + T Deserialize(IParser parser); + object? Deserialize(IParser parser); + + /// + /// Deserializes an object of the specified type. + /// + /// The from where to deserialize the object. + /// The static type of the object to deserialize. + /// Returns the deserialized object. + object? Deserialize(IParser parser, Type type); + } +} diff --git a/YamlDotNet/Serialization/IEventEmitter.cs b/YamlDotNet/Serialization/IEventEmitter.cs index f10d50259..9ed1b7b15 100644 --- a/YamlDotNet/Serialization/IEventEmitter.cs +++ b/YamlDotNet/Serialization/IEventEmitter.cs @@ -1,35 +1,35 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public interface IEventEmitter - { - void Emit(AliasEventInfo eventInfo, IEmitter emitter); - void Emit(ScalarEventInfo eventInfo, IEmitter emitter); - void Emit(MappingStartEventInfo eventInfo, IEmitter emitter); - void Emit(MappingEndEventInfo eventInfo, IEmitter emitter); - void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter); - void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public interface IEventEmitter + { + void Emit(AliasEventInfo eventInfo, IEmitter emitter); + void Emit(ScalarEventInfo eventInfo, IEmitter emitter); + void Emit(MappingStartEventInfo eventInfo, IEmitter emitter); + void Emit(MappingEndEventInfo eventInfo, IEmitter emitter); + void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter); + void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter); + } +} diff --git a/YamlDotNet/Serialization/INamingConvention.cs b/YamlDotNet/Serialization/INamingConvention.cs index 96456edc2..ab89a7a2f 100644 --- a/YamlDotNet/Serialization/INamingConvention.cs +++ b/YamlDotNet/Serialization/INamingConvention.cs @@ -1,32 +1,32 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - - -namespace YamlDotNet.Serialization -{ - /// - /// Translates property names according to a specific convention. - /// - public interface INamingConvention - { - string Apply(string value); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +namespace YamlDotNet.Serialization +{ + /// + /// Translates property names according to a specific convention. + /// + public interface INamingConvention + { + string Apply(string value); + } +} diff --git a/YamlDotNet/Serialization/INodeDeserializer.cs b/YamlDotNet/Serialization/INodeDeserializer.cs index ed3cb04f1..9e691fcdc 100644 --- a/YamlDotNet/Serialization/INodeDeserializer.cs +++ b/YamlDotNet/Serialization/INodeDeserializer.cs @@ -1,31 +1,31 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public interface INodeDeserializer - { - bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public interface INodeDeserializer + { + bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value); + } +} diff --git a/YamlDotNet/Serialization/INodeTypeResolver.cs b/YamlDotNet/Serialization/INodeTypeResolver.cs index f72afe577..c346ac8f4 100644 --- a/YamlDotNet/Serialization/INodeTypeResolver.cs +++ b/YamlDotNet/Serialization/INodeTypeResolver.cs @@ -1,40 +1,40 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization -{ - public interface INodeTypeResolver - { - /// - /// Determines the type of the specified node. - /// - /// The node to be deserialized. - /// The type that has been determined so far. - /// - /// true if has been resolved completely; - /// false if the next type should be invoked. - /// - bool Resolve(NodeEvent? nodeEvent, ref Type currentType); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization +{ + public interface INodeTypeResolver + { + /// + /// Determines the type of the specified node. + /// + /// The node to be deserialized. + /// The type that has been determined so far. + /// + /// true if has been resolved completely; + /// false if the next type should be invoked. + /// + bool Resolve(NodeEvent? nodeEvent, ref Type currentType); + } +} diff --git a/YamlDotNet/Serialization/IObjectDescriptor.cs b/YamlDotNet/Serialization/IObjectDescriptor.cs index 1f80e9a83..9864fe21b 100644 --- a/YamlDotNet/Serialization/IObjectDescriptor.cs +++ b/YamlDotNet/Serialization/IObjectDescriptor.cs @@ -1,67 +1,67 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - /// - /// Represents an object along with its type. - /// - public interface IObjectDescriptor - { - /// - /// A reference to the object. - /// - object? Value { get; } - - /// - /// The type that should be used when to interpret the . - /// - Type Type { get; } - - /// - /// The type of as determined by its container (e.g. a property). - /// - Type StaticType { get; } - - /// - /// The style to be used for scalars. - /// - ScalarStyle ScalarStyle { get; } - } - - public static class ObjectDescriptorExtensions - { - /// - /// Returns the Value property of the if it is not null. - /// This is useful in all places that the value must not be null. - /// - /// An object descriptor. - /// Thrown when the Value is null - /// - public static object NonNullValue(this IObjectDescriptor objectDescriptor) - { - return objectDescriptor.Value ?? throw new InvalidOperationException($"Attempted to use a IObjectDescriptor of type '{objectDescriptor.Type.FullName}' whose Value is null at a point whete it is invalid to do so. This may indicate a bug in YamlDotNet."); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + /// + /// Represents an object along with its type. + /// + public interface IObjectDescriptor + { + /// + /// A reference to the object. + /// + object? Value { get; } + + /// + /// The type that should be used when to interpret the . + /// + Type Type { get; } + + /// + /// The type of as determined by its container (e.g. a property). + /// + Type StaticType { get; } + + /// + /// The style to be used for scalars. + /// + ScalarStyle ScalarStyle { get; } + } + + public static class ObjectDescriptorExtensions + { + /// + /// Returns the Value property of the if it is not null. + /// This is useful in all places that the value must not be null. + /// + /// An object descriptor. + /// Thrown when the Value is null + /// + public static object NonNullValue(this IObjectDescriptor objectDescriptor) + { + return objectDescriptor.Value ?? throw new InvalidOperationException($"Attempted to use a IObjectDescriptor of type '{objectDescriptor.Type.FullName}' whose Value is null at a point whete it is invalid to do so. This may indicate a bug in YamlDotNet."); + } + } +} diff --git a/YamlDotNet/Serialization/IObjectFactory.cs b/YamlDotNet/Serialization/IObjectFactory.cs index c3ddfa5d9..a1b1defcd 100644 --- a/YamlDotNet/Serialization/IObjectFactory.cs +++ b/YamlDotNet/Serialization/IObjectFactory.cs @@ -1,39 +1,39 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Serialization -{ - /// - /// Creates instances of types. - /// - /// - /// This interface allows to provide a custom logic for creating instances during deserialization. - /// - public interface IObjectFactory - { - /// - /// Creates an instance of the specified type. - /// - object Create(Type type); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Serialization +{ + /// + /// Creates instances of types. + /// + /// + /// This interface allows to provide a custom logic for creating instances during deserialization. + /// + public interface IObjectFactory + { + /// + /// Creates an instance of the specified type. + /// + object Create(Type type); + } +} diff --git a/YamlDotNet/Serialization/IObjectGraphTraversalStrategy.cs b/YamlDotNet/Serialization/IObjectGraphTraversalStrategy.cs index 44e82ad4c..dece648f7 100644 --- a/YamlDotNet/Serialization/IObjectGraphTraversalStrategy.cs +++ b/YamlDotNet/Serialization/IObjectGraphTraversalStrategy.cs @@ -1,37 +1,37 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Serialization -{ - /// - /// Defines a strategy that walks through an object graph. - /// - public interface IObjectGraphTraversalStrategy - { - /// - /// Traverses the specified object graph. - /// - /// The graph. - /// An that is to be notified during the traversal. - /// A that will be passed to the . - void Traverse(IObjectDescriptor graph, IObjectGraphVisitor visitor, TContext context); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Serialization +{ + /// + /// Defines a strategy that walks through an object graph. + /// + public interface IObjectGraphTraversalStrategy + { + /// + /// Traverses the specified object graph. + /// + /// The graph. + /// An that is to be notified during the traversal. + /// A that will be passed to the . + void Traverse(IObjectDescriptor graph, IObjectGraphVisitor visitor, TContext context); + } +} diff --git a/YamlDotNet/Serialization/IObjectGraphVisitor.cs b/YamlDotNet/Serialization/IObjectGraphVisitor.cs index 93f889e63..87455f107 100644 --- a/YamlDotNet/Serialization/IObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/IObjectGraphVisitor.cs @@ -1,99 +1,99 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Serialization -{ - /// - /// Defined the interface of a type that can be notified during an object graph traversal. - /// - public interface IObjectGraphVisitor - { - /// - /// Indicates whether the specified value should be entered. This allows the visitor to - /// override the handling of a particular object or type. - /// - /// The value that is about to be entered. - /// The context that this implementation depend on. - /// If the value is to be entered, returns true; otherwise returns false; - bool Enter(IObjectDescriptor value, TContext context); - - /// - /// Indicates whether the specified mapping should be entered. This allows the visitor to - /// override the handling of a particular pair. - /// - /// The key of the mapping that is about to be entered. - /// The value of the mapping that is about to be entered. - /// The context that this implementation depend on. - /// If the mapping is to be entered, returns true; otherwise returns false; - bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, TContext context); - - /// - /// Indicates whether the specified mapping should be entered. This allows the visitor to - /// override the handling of a particular pair. This overload should be invoked when the - /// mapping is produced by an object's property. - /// - /// The that provided access to . - /// The value of the mapping that is about to be entered. - /// The context that this implementation depend on. - /// If the mapping is to be entered, returns true; otherwise returns false; - bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, TContext context); - - /// - /// Notifies the visitor that a scalar value has been encountered. - /// - /// The value of the scalar. - /// The context that this implementation depend on. - void VisitScalar(IObjectDescriptor scalar, TContext context); - - /// - /// Notifies the visitor that the traversal of a mapping is about to begin. - /// - /// The value that corresponds to the mapping. - /// The static type of the keys of the mapping. - /// The static type of the values of the mapping. - /// The context that this implementation depend on. - void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, TContext context); - - /// - /// Notifies the visitor that the traversal of a mapping has ended. - /// - /// The value that corresponds to the mapping. - /// The context that this implementation depend on. - void VisitMappingEnd(IObjectDescriptor mapping, TContext context); - - /// - /// Notifies the visitor that the traversal of a sequence is about to begin. - /// - /// The value that corresponds to the sequence. - /// The static type of the elements of the sequence. - /// The context that this implementation depend on. - void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, TContext context); - - /// - /// Notifies the visitor that the traversal of a sequence has ended. - /// - /// The value that corresponds to the sequence. - /// The context that this implementation depend on. - void VisitSequenceEnd(IObjectDescriptor sequence, TContext context); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Serialization +{ + /// + /// Defined the interface of a type that can be notified during an object graph traversal. + /// + public interface IObjectGraphVisitor + { + /// + /// Indicates whether the specified value should be entered. This allows the visitor to + /// override the handling of a particular object or type. + /// + /// The value that is about to be entered. + /// The context that this implementation depend on. + /// If the value is to be entered, returns true; otherwise returns false; + bool Enter(IObjectDescriptor value, TContext context); + + /// + /// Indicates whether the specified mapping should be entered. This allows the visitor to + /// override the handling of a particular pair. + /// + /// The key of the mapping that is about to be entered. + /// The value of the mapping that is about to be entered. + /// The context that this implementation depend on. + /// If the mapping is to be entered, returns true; otherwise returns false; + bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, TContext context); + + /// + /// Indicates whether the specified mapping should be entered. This allows the visitor to + /// override the handling of a particular pair. This overload should be invoked when the + /// mapping is produced by an object's property. + /// + /// The that provided access to . + /// The value of the mapping that is about to be entered. + /// The context that this implementation depend on. + /// If the mapping is to be entered, returns true; otherwise returns false; + bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, TContext context); + + /// + /// Notifies the visitor that a scalar value has been encountered. + /// + /// The value of the scalar. + /// The context that this implementation depend on. + void VisitScalar(IObjectDescriptor scalar, TContext context); + + /// + /// Notifies the visitor that the traversal of a mapping is about to begin. + /// + /// The value that corresponds to the mapping. + /// The static type of the keys of the mapping. + /// The static type of the values of the mapping. + /// The context that this implementation depend on. + void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, TContext context); + + /// + /// Notifies the visitor that the traversal of a mapping has ended. + /// + /// The value that corresponds to the mapping. + /// The context that this implementation depend on. + void VisitMappingEnd(IObjectDescriptor mapping, TContext context); + + /// + /// Notifies the visitor that the traversal of a sequence is about to begin. + /// + /// The value that corresponds to the sequence. + /// The static type of the elements of the sequence. + /// The context that this implementation depend on. + void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, TContext context); + + /// + /// Notifies the visitor that the traversal of a sequence has ended. + /// + /// The value that corresponds to the sequence. + /// The context that this implementation depend on. + void VisitSequenceEnd(IObjectDescriptor sequence, TContext context); + } +} diff --git a/YamlDotNet/Serialization/IPropertyDescriptor.cs b/YamlDotNet/Serialization/IPropertyDescriptor.cs index dd8915dcb..d680684f5 100644 --- a/YamlDotNet/Serialization/IPropertyDescriptor.cs +++ b/YamlDotNet/Serialization/IPropertyDescriptor.cs @@ -1,41 +1,41 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public interface IPropertyDescriptor - { - string Name { get; } - bool CanWrite { get; } - Type Type { get; } - Type? TypeOverride { get; set; } - int Order { get; set; } - ScalarStyle ScalarStyle { get; set; } - - T GetCustomAttribute() where T : Attribute; - - IObjectDescriptor Read(object target); - void Write(object target, object? value); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public interface IPropertyDescriptor + { + string Name { get; } + bool CanWrite { get; } + Type Type { get; } + Type? TypeOverride { get; set; } + int Order { get; set; } + ScalarStyle ScalarStyle { get; set; } + + T GetCustomAttribute() where T : Attribute; + + IObjectDescriptor Read(object target); + void Write(object target, object? value); + } +} diff --git a/YamlDotNet/Serialization/IRegistrationLocationSelectionSyntax.cs b/YamlDotNet/Serialization/IRegistrationLocationSelectionSyntax.cs index 0490a80c9..d1d50fa3f 100644 --- a/YamlDotNet/Serialization/IRegistrationLocationSelectionSyntax.cs +++ b/YamlDotNet/Serialization/IRegistrationLocationSelectionSyntax.cs @@ -1,59 +1,59 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Serialization -{ - public interface IRegistrationLocationSelectionSyntax - { - /// - /// Registers the component in place of the already registered component of type . - /// - void InsteadOf() where TRegistrationType : TBaseRegistrationType; - - /// - /// Registers the component before the already registered component of type . - /// - void Before() where TRegistrationType : TBaseRegistrationType; - - /// - /// Registers the component after the already registered component of type . - /// - void After() where TRegistrationType : TBaseRegistrationType; - - /// - /// Registers the component before every other previously registered component. - /// - void OnTop(); - - /// - /// Registers the component after every other previously registered component. - /// - void OnBottom(); - } - - public interface ITrackingRegistrationLocationSelectionSyntax - { - /// - /// Registers the component in place of the already registered component of type . - /// - void InsteadOf() where TRegistrationType : TBaseRegistrationType; - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Serialization +{ + public interface IRegistrationLocationSelectionSyntax + { + /// + /// Registers the component in place of the already registered component of type . + /// + void InsteadOf() where TRegistrationType : TBaseRegistrationType; + + /// + /// Registers the component before the already registered component of type . + /// + void Before() where TRegistrationType : TBaseRegistrationType; + + /// + /// Registers the component after the already registered component of type . + /// + void After() where TRegistrationType : TBaseRegistrationType; + + /// + /// Registers the component before every other previously registered component. + /// + void OnTop(); + + /// + /// Registers the component after every other previously registered component. + /// + void OnBottom(); + } + + public interface ITrackingRegistrationLocationSelectionSyntax + { + /// + /// Registers the component in place of the already registered component of type . + /// + void InsteadOf() where TRegistrationType : TBaseRegistrationType; + } +} diff --git a/YamlDotNet/Serialization/ISerializer.cs b/YamlDotNet/Serialization/ISerializer.cs index ab3336574..b7b884728 100644 --- a/YamlDotNet/Serialization/ISerializer.cs +++ b/YamlDotNet/Serialization/ISerializer.cs @@ -1,66 +1,66 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public interface ISerializer - { - /// - /// Serializes the specified object. - /// - /// The where to serialize the object. - /// The object to serialize. - void Serialize(TextWriter writer, object graph); - - /// - /// Serializes the specified object into a string. - /// - /// The object to serialize. - string Serialize(object graph); - - /// - /// Serializes the specified object. - /// - /// The where to serialize the object. - /// The object to serialize. - /// The static type of the object to serialize. - void Serialize(TextWriter writer, object graph, Type type); - - /// - /// Serializes the specified object. - /// - /// The where to serialize the object. - /// The object to serialize. - void Serialize(IEmitter emitter, object graph); - - /// - /// Serializes the specified object. - /// - /// The where to serialize the object. - /// The object to serialize. - /// The static type of the object to serialize. - void Serialize(IEmitter emitter, object graph, Type type); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.IO; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public interface ISerializer + { + /// + /// Serializes the specified object. + /// + /// The where to serialize the object. + /// The object to serialize. + void Serialize(TextWriter writer, object graph); + + /// + /// Serializes the specified object into a string. + /// + /// The object to serialize. + string Serialize(object graph); + + /// + /// Serializes the specified object. + /// + /// The where to serialize the object. + /// The object to serialize. + /// The static type of the object to serialize. + void Serialize(TextWriter writer, object graph, Type type); + + /// + /// Serializes the specified object. + /// + /// The where to serialize the object. + /// The object to serialize. + void Serialize(IEmitter emitter, object graph); + + /// + /// Serializes the specified object. + /// + /// The where to serialize the object. + /// The object to serialize. + /// The static type of the object to serialize. + void Serialize(IEmitter emitter, object graph, Type type); + } +} diff --git a/YamlDotNet/Serialization/ITypeInspector.cs b/YamlDotNet/Serialization/ITypeInspector.cs index ba914b3c0..7e73796df 100644 --- a/YamlDotNet/Serialization/ITypeInspector.cs +++ b/YamlDotNet/Serialization/ITypeInspector.cs @@ -1,54 +1,54 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace YamlDotNet.Serialization -{ - /// - /// Provides access to the properties of a type. - /// - public interface ITypeInspector - { - /// - /// Gets all properties of the specified type. - /// - /// The type whose properties are to be enumerated. - /// The actual object of type whose properties are to be enumerated. Can be null. - /// - IEnumerable GetProperties(Type type, object? container); - - /// - /// Gets the property of the type with the specified name. - /// - /// The type whose properties are to be searched. - /// The actual object of type whose properties are to be searched. Can be null. - /// The name of the property. - /// - /// Determines if an exception or null should be returned if can't be - /// found in - /// - /// - IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace YamlDotNet.Serialization +{ + /// + /// Provides access to the properties of a type. + /// + public interface ITypeInspector + { + /// + /// Gets all properties of the specified type. + /// + /// The type whose properties are to be enumerated. + /// The actual object of type whose properties are to be enumerated. Can be null. + /// + IEnumerable GetProperties(Type type, object? container); + + /// + /// Gets the property of the type with the specified name. + /// + /// The type whose properties are to be searched. + /// The actual object of type whose properties are to be searched. Can be null. + /// The name of the property. + /// + /// Determines if an exception or null should be returned if can't be + /// found in + /// + /// + IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched); + } +} diff --git a/YamlDotNet/Serialization/ITypeResolver.cs b/YamlDotNet/Serialization/ITypeResolver.cs index fd3efc6b4..24fbe06a2 100644 --- a/YamlDotNet/Serialization/ITypeResolver.cs +++ b/YamlDotNet/Serialization/ITypeResolver.cs @@ -1,33 +1,33 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Serialization -{ - /// - /// Resolves the type of values. - /// - public interface ITypeResolver - { - Type Resolve(Type staticType, object? actualValue); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Serialization +{ + /// + /// Resolves the type of values. + /// + public interface ITypeResolver + { + Type Resolve(Type staticType, object? actualValue); + } +} diff --git a/YamlDotNet/Serialization/IValueDeserializer.cs b/YamlDotNet/Serialization/IValueDeserializer.cs index 8ff5fe32b..35d64e905 100644 --- a/YamlDotNet/Serialization/IValueDeserializer.cs +++ b/YamlDotNet/Serialization/IValueDeserializer.cs @@ -1,32 +1,32 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization -{ - public interface IValueDeserializer - { - object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization +{ + public interface IValueDeserializer + { + object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer); + } +} diff --git a/YamlDotNet/Serialization/IValuePromise.cs b/YamlDotNet/Serialization/IValuePromise.cs index 3c40c0186..f16ea198c 100644 --- a/YamlDotNet/Serialization/IValuePromise.cs +++ b/YamlDotNet/Serialization/IValuePromise.cs @@ -1,30 +1,30 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Serialization -{ - public interface IValuePromise - { - event Action ValueAvailable; - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Serialization +{ + public interface IValuePromise + { + event Action ValueAvailable; + } +} diff --git a/YamlDotNet/Serialization/IValueSerializer.cs b/YamlDotNet/Serialization/IValueSerializer.cs index e5c8ac3a0..a5c1ab682 100644 --- a/YamlDotNet/Serialization/IValueSerializer.cs +++ b/YamlDotNet/Serialization/IValueSerializer.cs @@ -1,31 +1,31 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public interface IValueSerializer - { - void SerializeValue(IEmitter emitter, object? value, Type? type); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public interface IValueSerializer + { + void SerializeValue(IEmitter emitter, object? value, Type? type); + } +} diff --git a/YamlDotNet/Serialization/IYamlConvertible.cs b/YamlDotNet/Serialization/IYamlConvertible.cs index 8d1318574..936773ab1 100644 --- a/YamlDotNet/Serialization/IYamlConvertible.cs +++ b/YamlDotNet/Serialization/IYamlConvertible.cs @@ -1,67 +1,67 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - /// - /// Allows an object to customize how it is serialized and deserialized. - /// - public interface IYamlConvertible - { - /// - /// Reads this object's state from a YAML parser. - /// - /// The parser where the object's state should be read from. - /// The type that the deserializer is expecting. - /// - /// A function that will use the current deserializer - /// to read an object of the given type from the parser. - /// - void Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer); - - /// - /// Writes this object's state to a YAML emitter. - /// - /// The emitter where the object's state should be written to. - /// A function that will use the current serializer to write an object to the emitter. - void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer); - } - - /// - /// Represents a function that is used to deserialize an object of the given type. - /// - /// The type that the deserializer should read. - /// Returns the object that was deserialized. - public delegate object? ObjectDeserializer(Type type); - - /// - /// Represents a function that is used to serialize an object of the given type. - /// - /// The object to be serialized. - /// - /// The type that should be considered when emitting the object. - /// If null, the actual type of the is used. - /// - public delegate void ObjectSerializer(object? value, Type? type = null); -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + /// + /// Allows an object to customize how it is serialized and deserialized. + /// + public interface IYamlConvertible + { + /// + /// Reads this object's state from a YAML parser. + /// + /// The parser where the object's state should be read from. + /// The type that the deserializer is expecting. + /// + /// A function that will use the current deserializer + /// to read an object of the given type from the parser. + /// + void Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer); + + /// + /// Writes this object's state to a YAML emitter. + /// + /// The emitter where the object's state should be written to. + /// A function that will use the current serializer to write an object to the emitter. + void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer); + } + + /// + /// Represents a function that is used to deserialize an object of the given type. + /// + /// The type that the deserializer should read. + /// Returns the object that was deserialized. + public delegate object? ObjectDeserializer(Type type); + + /// + /// Represents a function that is used to serialize an object of the given type. + /// + /// The object to be serialized. + /// + /// The type that should be considered when emitting the object. + /// If null, the actual type of the is used. + /// + public delegate void ObjectSerializer(object? value, Type? type = null); +} diff --git a/YamlDotNet/Serialization/IYamlSerializable.cs b/YamlDotNet/Serialization/IYamlSerializable.cs index 0645545ee..406cac2ac 100644 --- a/YamlDotNet/Serialization/IYamlSerializable.cs +++ b/YamlDotNet/Serialization/IYamlSerializable.cs @@ -1,43 +1,43 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - /// - /// Allows an object to customize how it is serialized and deserialized. - /// - [Obsolete("Please use IYamlConvertible instead")] - public interface IYamlSerializable - { - /// - /// Reads this object's state from a YAML parser. - /// - void ReadYaml(IParser parser); - - /// - /// Writes this object's state to a YAML emitter. - /// - void WriteYaml(IEmitter emitter); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + /// + /// Allows an object to customize how it is serialized and deserialized. + /// + [Obsolete("Please use IYamlConvertible instead")] + public interface IYamlSerializable + { + /// + /// Reads this object's state from a YAML parser. + /// + void ReadYaml(IParser parser); + + /// + /// Writes this object's state to a YAML emitter. + /// + void WriteYaml(IEmitter emitter); + } +} diff --git a/YamlDotNet/Serialization/IYamlTypeConverter.cs b/YamlDotNet/Serialization/IYamlTypeConverter.cs index fdaabaa48..497657b01 100644 --- a/YamlDotNet/Serialization/IYamlTypeConverter.cs +++ b/YamlDotNet/Serialization/IYamlTypeConverter.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - /// - /// Allows to customize how a type is serialized and deserialized. - /// - public interface IYamlTypeConverter - { - /// - /// Gets a value indicating whether the current converter supports converting the specified type. - /// - bool Accepts(Type type); - - /// - /// Reads an object's state from a YAML parser. - /// - object? ReadYaml(IParser parser, Type type); - - /// - /// Writes the specified object's state to a YAML emitter. - /// - void WriteYaml(IEmitter emitter, object? value, Type type); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + /// + /// Allows to customize how a type is serialized and deserialized. + /// + public interface IYamlTypeConverter + { + /// + /// Gets a value indicating whether the current converter supports converting the specified type. + /// + bool Accepts(Type type); + + /// + /// Reads an object's state from a YAML parser. + /// + object? ReadYaml(IParser parser, Type type); + + /// + /// Writes the specified object's state to a YAML emitter. + /// + void WriteYaml(IEmitter emitter, object? value, Type type); + } +} diff --git a/YamlDotNet/Serialization/LazyComponentRegistrationList.cs b/YamlDotNet/Serialization/LazyComponentRegistrationList.cs index 4692af08b..12f736406 100644 --- a/YamlDotNet/Serialization/LazyComponentRegistrationList.cs +++ b/YamlDotNet/Serialization/LazyComponentRegistrationList.cs @@ -1,237 +1,237 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -namespace YamlDotNet.Serialization -{ - internal sealed class LazyComponentRegistrationList : IEnumerable> - { - private readonly List entries = new List(); - - public LazyComponentRegistrationList Clone() - { - var clone = new LazyComponentRegistrationList(); - foreach (var entry in entries) - { - clone.entries.Add(entry); - } - return clone; - } - - public sealed class LazyComponentRegistration - { - public readonly Type ComponentType; - public readonly Func Factory; - - public LazyComponentRegistration(Type componentType, Func factory) - { - ComponentType = componentType; - Factory = factory; - } - } - - public sealed class TrackingLazyComponentRegistration - { - public readonly Type ComponentType; - public readonly Func Factory; - - public TrackingLazyComponentRegistration(Type componentType, Func factory) - { - ComponentType = componentType; - Factory = factory; - } - } - - public void Add(Type componentType, Func factory) - { - entries.Add(new LazyComponentRegistration(componentType, factory)); - } - - public void Remove(Type componentType) - { - for (var i = 0; i < entries.Count; ++i) - { - if (entries[i].ComponentType == componentType) - { - entries.RemoveAt(i); - return; - } - } - - throw new KeyNotFoundException($"A component registration of type '{componentType.FullName}' was not found."); - } - - public int Count => entries.Count; - - public IEnumerable> InReverseOrder - { - get - { - for (var i = entries.Count - 1; i >= 0; --i) - { - yield return entries[i].Factory; - } - } - } - - public IRegistrationLocationSelectionSyntax CreateRegistrationLocationSelector( - Type componentType, - Func factory - ) - { - return new RegistrationLocationSelector( - this, - new LazyComponentRegistration(componentType, factory) - ); - } - - public ITrackingRegistrationLocationSelectionSyntax CreateTrackingRegistrationLocationSelector( - Type componentType, - Func factory - ) - { - return new TrackingRegistrationLocationSelector( - this, - new TrackingLazyComponentRegistration(componentType, factory) - ); - } - - public IEnumerator> GetEnumerator() - { - return entries.Select(e => e.Factory).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - private int IndexOfRegistration(Type registrationType) - { - for (var i = 0; i < entries.Count; ++i) - { - if (registrationType == entries[i].ComponentType) - { - return i; - } - } - return -1; - } - - private void EnsureNoDuplicateRegistrationType(Type componentType) - { - if (IndexOfRegistration(componentType) != -1) - { - throw new InvalidOperationException($"A component of type '{componentType.FullName}' has already been registered."); - } - } - - private int EnsureRegistrationExists() - { - var registrationIndex = IndexOfRegistration(typeof(TRegistrationType)); - if (registrationIndex == -1) - { - throw new InvalidOperationException($"A component of type '{typeof(TRegistrationType).FullName}' has not been registered."); - } - return registrationIndex; - } - - private class RegistrationLocationSelector : IRegistrationLocationSelectionSyntax - { - private readonly LazyComponentRegistrationList registrations; - private readonly LazyComponentRegistration newRegistration; - - public RegistrationLocationSelector(LazyComponentRegistrationList registrations, LazyComponentRegistration newRegistration) - { - this.registrations = registrations; - this.newRegistration = newRegistration; - } - - void IRegistrationLocationSelectionSyntax.InsteadOf() - { - if (newRegistration.ComponentType != typeof(TRegistrationType)) - { - registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); - } - - var registrationIndex = registrations.EnsureRegistrationExists(); - registrations.entries[registrationIndex] = newRegistration; - } - - void IRegistrationLocationSelectionSyntax.After() - { - registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); - var registrationIndex = registrations.EnsureRegistrationExists(); - registrations.entries.Insert(registrationIndex + 1, newRegistration); - } - - void IRegistrationLocationSelectionSyntax.Before() - { - registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); - var registrationIndex = registrations.EnsureRegistrationExists(); - registrations.entries.Insert(registrationIndex, newRegistration); - } - - void IRegistrationLocationSelectionSyntax.OnBottom() - { - registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); - registrations.entries.Add(newRegistration); - } - - void IRegistrationLocationSelectionSyntax.OnTop() - { - registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); - registrations.entries.Insert(0, newRegistration); - } - } - - private class TrackingRegistrationLocationSelector : ITrackingRegistrationLocationSelectionSyntax - { - private readonly LazyComponentRegistrationList registrations; - private readonly TrackingLazyComponentRegistration newRegistration; - - public TrackingRegistrationLocationSelector(LazyComponentRegistrationList registrations, TrackingLazyComponentRegistration newRegistration) - { - this.registrations = registrations; - this.newRegistration = newRegistration; - } - - void ITrackingRegistrationLocationSelectionSyntax.InsteadOf() - { - if (newRegistration.ComponentType != typeof(TRegistrationType)) - { - registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); - } - - var registrationIndex = registrations.EnsureRegistrationExists(); - var innerComponentFactory = registrations.entries[registrationIndex].Factory; - registrations.entries[registrationIndex] = new LazyComponentRegistration( - newRegistration.ComponentType, - arg => newRegistration.Factory(innerComponentFactory(arg), arg) - ); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace YamlDotNet.Serialization +{ + internal sealed class LazyComponentRegistrationList : IEnumerable> + { + private readonly List entries = new List(); + + public LazyComponentRegistrationList Clone() + { + var clone = new LazyComponentRegistrationList(); + foreach (var entry in entries) + { + clone.entries.Add(entry); + } + return clone; + } + + public sealed class LazyComponentRegistration + { + public readonly Type ComponentType; + public readonly Func Factory; + + public LazyComponentRegistration(Type componentType, Func factory) + { + ComponentType = componentType; + Factory = factory; + } + } + + public sealed class TrackingLazyComponentRegistration + { + public readonly Type ComponentType; + public readonly Func Factory; + + public TrackingLazyComponentRegistration(Type componentType, Func factory) + { + ComponentType = componentType; + Factory = factory; + } + } + + public void Add(Type componentType, Func factory) + { + entries.Add(new LazyComponentRegistration(componentType, factory)); + } + + public void Remove(Type componentType) + { + for (var i = 0; i < entries.Count; ++i) + { + if (entries[i].ComponentType == componentType) + { + entries.RemoveAt(i); + return; + } + } + + throw new KeyNotFoundException($"A component registration of type '{componentType.FullName}' was not found."); + } + + public int Count => entries.Count; + + public IEnumerable> InReverseOrder + { + get + { + for (var i = entries.Count - 1; i >= 0; --i) + { + yield return entries[i].Factory; + } + } + } + + public IRegistrationLocationSelectionSyntax CreateRegistrationLocationSelector( + Type componentType, + Func factory + ) + { + return new RegistrationLocationSelector( + this, + new LazyComponentRegistration(componentType, factory) + ); + } + + public ITrackingRegistrationLocationSelectionSyntax CreateTrackingRegistrationLocationSelector( + Type componentType, + Func factory + ) + { + return new TrackingRegistrationLocationSelector( + this, + new TrackingLazyComponentRegistration(componentType, factory) + ); + } + + public IEnumerator> GetEnumerator() + { + return entries.Select(e => e.Factory).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private int IndexOfRegistration(Type registrationType) + { + for (var i = 0; i < entries.Count; ++i) + { + if (registrationType == entries[i].ComponentType) + { + return i; + } + } + return -1; + } + + private void EnsureNoDuplicateRegistrationType(Type componentType) + { + if (IndexOfRegistration(componentType) != -1) + { + throw new InvalidOperationException($"A component of type '{componentType.FullName}' has already been registered."); + } + } + + private int EnsureRegistrationExists() + { + var registrationIndex = IndexOfRegistration(typeof(TRegistrationType)); + if (registrationIndex == -1) + { + throw new InvalidOperationException($"A component of type '{typeof(TRegistrationType).FullName}' has not been registered."); + } + return registrationIndex; + } + + private class RegistrationLocationSelector : IRegistrationLocationSelectionSyntax + { + private readonly LazyComponentRegistrationList registrations; + private readonly LazyComponentRegistration newRegistration; + + public RegistrationLocationSelector(LazyComponentRegistrationList registrations, LazyComponentRegistration newRegistration) + { + this.registrations = registrations; + this.newRegistration = newRegistration; + } + + void IRegistrationLocationSelectionSyntax.InsteadOf() + { + if (newRegistration.ComponentType != typeof(TRegistrationType)) + { + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); + } + + var registrationIndex = registrations.EnsureRegistrationExists(); + registrations.entries[registrationIndex] = newRegistration; + } + + void IRegistrationLocationSelectionSyntax.After() + { + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); + var registrationIndex = registrations.EnsureRegistrationExists(); + registrations.entries.Insert(registrationIndex + 1, newRegistration); + } + + void IRegistrationLocationSelectionSyntax.Before() + { + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); + var registrationIndex = registrations.EnsureRegistrationExists(); + registrations.entries.Insert(registrationIndex, newRegistration); + } + + void IRegistrationLocationSelectionSyntax.OnBottom() + { + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); + registrations.entries.Add(newRegistration); + } + + void IRegistrationLocationSelectionSyntax.OnTop() + { + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); + registrations.entries.Insert(0, newRegistration); + } + } + + private class TrackingRegistrationLocationSelector : ITrackingRegistrationLocationSelectionSyntax + { + private readonly LazyComponentRegistrationList registrations; + private readonly TrackingLazyComponentRegistration newRegistration; + + public TrackingRegistrationLocationSelector(LazyComponentRegistrationList registrations, TrackingLazyComponentRegistration newRegistration) + { + this.registrations = registrations; + this.newRegistration = newRegistration; + } + + void ITrackingRegistrationLocationSelectionSyntax.InsteadOf() + { + if (newRegistration.ComponentType != typeof(TRegistrationType)) + { + registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); + } + + var registrationIndex = registrations.EnsureRegistrationExists(); + var innerComponentFactory = registrations.entries[registrationIndex].Factory; + registrations.entries[registrationIndex] = new LazyComponentRegistration( + newRegistration.ComponentType, + arg => newRegistration.Factory(innerComponentFactory(arg), arg) + ); + } + } + } +} diff --git a/YamlDotNet/Serialization/LazyComponentRegistrationListExtensions.cs b/YamlDotNet/Serialization/LazyComponentRegistrationListExtensions.cs index f3e653d32..e47810b73 100644 --- a/YamlDotNet/Serialization/LazyComponentRegistrationListExtensions.cs +++ b/YamlDotNet/Serialization/LazyComponentRegistrationListExtensions.cs @@ -1,64 +1,64 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace YamlDotNet.Serialization -{ - internal static class LazyComponentRegistrationListExtensions - { - public static TComponent BuildComponentChain(this LazyComponentRegistrationList registrations, TComponent innerComponent) - { - var outerComponent = registrations.InReverseOrder.Aggregate( - innerComponent, - (inner, factory) => factory(inner) - ); - - return outerComponent; - } - - public static TComponent BuildComponentChain(this LazyComponentRegistrationList registrations, TComponent innerComponent, Func argumentBuilder) - { - var outerComponent = registrations.InReverseOrder.Aggregate( - innerComponent, - (inner, factory) => factory(argumentBuilder(inner)) - ); - - return outerComponent; - } - - public static List BuildComponentList(this LazyComponentRegistrationList registrations) - { - return registrations - .Select(factory => factory(default)) - .ToList(); - } - - public static List BuildComponentList(this LazyComponentRegistrationList registrations, TArgument argument) - { - return registrations - .Select(factory => factory(argument)) - .ToList(); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace YamlDotNet.Serialization +{ + internal static class LazyComponentRegistrationListExtensions + { + public static TComponent BuildComponentChain(this LazyComponentRegistrationList registrations, TComponent innerComponent) + { + var outerComponent = registrations.InReverseOrder.Aggregate( + innerComponent, + (inner, factory) => factory(inner) + ); + + return outerComponent; + } + + public static TComponent BuildComponentChain(this LazyComponentRegistrationList registrations, TComponent innerComponent, Func argumentBuilder) + { + var outerComponent = registrations.InReverseOrder.Aggregate( + innerComponent, + (inner, factory) => factory(argumentBuilder(inner)) + ); + + return outerComponent; + } + + public static List BuildComponentList(this LazyComponentRegistrationList registrations) + { + return registrations + .Select(factory => factory(default)) + .ToList(); + } + + public static List BuildComponentList(this LazyComponentRegistrationList registrations, TArgument argument) + { + return registrations + .Select(factory => factory(argument)) + .ToList(); + } + } +} diff --git a/YamlDotNet/Serialization/NamingConventions/CamelCaseNamingConvention.cs b/YamlDotNet/Serialization/NamingConventions/CamelCaseNamingConvention.cs index f6f06946d..c56846f3b 100644 --- a/YamlDotNet/Serialization/NamingConventions/CamelCaseNamingConvention.cs +++ b/YamlDotNet/Serialization/NamingConventions/CamelCaseNamingConvention.cs @@ -1,46 +1,46 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NamingConventions -{ - /// - /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to - /// camel case (thisIsATest). Camel case is the same as Pascal case, except the first letter - /// is lowercase. - /// - public sealed class CamelCaseNamingConvention : INamingConvention - { - [Obsolete("Use the Instance static field instead of creating new instances")] - public CamelCaseNamingConvention() { } - - public string Apply(string value) - { - return value.ToCamelCase(); - } - -#pragma warning disable CS0618 // Type or member is obsolete - public static readonly INamingConvention Instance = new CamelCaseNamingConvention(); -#pragma warning restore CS0618 // Type or member is obsolete - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NamingConventions +{ + /// + /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to + /// camel case (thisIsATest). Camel case is the same as Pascal case, except the first letter + /// is lowercase. + /// + public sealed class CamelCaseNamingConvention : INamingConvention + { + [Obsolete("Use the Instance static field instead of creating new instances")] + public CamelCaseNamingConvention() { } + + public string Apply(string value) + { + return value.ToCamelCase(); + } + +#pragma warning disable CS0618 // Type or member is obsolete + public static readonly INamingConvention Instance = new CamelCaseNamingConvention(); +#pragma warning restore CS0618 // Type or member is obsolete + } +} diff --git a/YamlDotNet/Serialization/NamingConventions/HyphenatedNamingConvention.cs b/YamlDotNet/Serialization/NamingConventions/HyphenatedNamingConvention.cs index 270947708..78f15d500 100644 --- a/YamlDotNet/Serialization/NamingConventions/HyphenatedNamingConvention.cs +++ b/YamlDotNet/Serialization/NamingConventions/HyphenatedNamingConvention.cs @@ -1,44 +1,44 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NamingConventions -{ - /// - /// Convert the string from camelcase (thisIsATest) to a hyphenated (this-is-a-test) string - /// - public sealed class HyphenatedNamingConvention : INamingConvention - { - [Obsolete("Use the Instance static field instead of creating new instances")] - public HyphenatedNamingConvention() { } - - public string Apply(string value) - { - return value.FromCamelCase("-"); - } - -#pragma warning disable CS0618 // Type or member is obsolete - public static readonly INamingConvention Instance = new HyphenatedNamingConvention(); -#pragma warning restore CS0618 // Type or member is obsolete - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NamingConventions +{ + /// + /// Convert the string from camelcase (thisIsATest) to a hyphenated (this-is-a-test) string + /// + public sealed class HyphenatedNamingConvention : INamingConvention + { + [Obsolete("Use the Instance static field instead of creating new instances")] + public HyphenatedNamingConvention() { } + + public string Apply(string value) + { + return value.FromCamelCase("-"); + } + +#pragma warning disable CS0618 // Type or member is obsolete + public static readonly INamingConvention Instance = new HyphenatedNamingConvention(); +#pragma warning restore CS0618 // Type or member is obsolete + } +} diff --git a/YamlDotNet/Serialization/NamingConventions/LowerCaseNamingConvention.cs b/YamlDotNet/Serialization/NamingConventions/LowerCaseNamingConvention.cs index bb91d8e1f..39e7f92b2 100644 --- a/YamlDotNet/Serialization/NamingConventions/LowerCaseNamingConvention.cs +++ b/YamlDotNet/Serialization/NamingConventions/LowerCaseNamingConvention.cs @@ -1,42 +1,42 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NamingConventions -{ - /// - /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to - /// pascal case (ThisIsATest). Pascal case is the same as camel case, except the first letter - /// is uppercase. - /// - public sealed class LowerCaseNamingConvention : INamingConvention - { - private LowerCaseNamingConvention() { } - - public string Apply(string value) - { - return value.ToCamelCase().ToLower(); - } - - public static readonly INamingConvention Instance = new LowerCaseNamingConvention(); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NamingConventions +{ + /// + /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to + /// pascal case (ThisIsATest). Pascal case is the same as camel case, except the first letter + /// is uppercase. + /// + public sealed class LowerCaseNamingConvention : INamingConvention + { + private LowerCaseNamingConvention() { } + + public string Apply(string value) + { + return value.ToCamelCase().ToLower(); + } + + public static readonly INamingConvention Instance = new LowerCaseNamingConvention(); + } +} diff --git a/YamlDotNet/Serialization/NamingConventions/NullNamingConvention.cs b/YamlDotNet/Serialization/NamingConventions/NullNamingConvention.cs index dd623ca39..2bddbf84b 100644 --- a/YamlDotNet/Serialization/NamingConventions/NullNamingConvention.cs +++ b/YamlDotNet/Serialization/NamingConventions/NullNamingConvention.cs @@ -1,44 +1,44 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - - -using System; - -namespace YamlDotNet.Serialization.NamingConventions -{ - /// - /// Performs no naming conversion. - /// - public sealed class NullNamingConvention : INamingConvention - { - [Obsolete("Use the Instance static field instead of creating new instances")] - public NullNamingConvention() { } - - public string Apply(string value) - { - return value; - } - -#pragma warning disable CS0618 // Type or member is obsolete - public static readonly INamingConvention Instance = new NullNamingConvention(); -#pragma warning restore CS0618 // Type or member is obsolete - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +using System; + +namespace YamlDotNet.Serialization.NamingConventions +{ + /// + /// Performs no naming conversion. + /// + public sealed class NullNamingConvention : INamingConvention + { + [Obsolete("Use the Instance static field instead of creating new instances")] + public NullNamingConvention() { } + + public string Apply(string value) + { + return value; + } + +#pragma warning disable CS0618 // Type or member is obsolete + public static readonly INamingConvention Instance = new NullNamingConvention(); +#pragma warning restore CS0618 // Type or member is obsolete + } +} diff --git a/YamlDotNet/Serialization/NamingConventions/PascalCaseNamingConvention.cs b/YamlDotNet/Serialization/NamingConventions/PascalCaseNamingConvention.cs index fe5cbfe56..5fefd962d 100644 --- a/YamlDotNet/Serialization/NamingConventions/PascalCaseNamingConvention.cs +++ b/YamlDotNet/Serialization/NamingConventions/PascalCaseNamingConvention.cs @@ -1,46 +1,46 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NamingConventions -{ - /// - /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to - /// pascal case (ThisIsATest). Pascal case is the same as camel case, except the first letter - /// is uppercase. - /// - public sealed class PascalCaseNamingConvention : INamingConvention - { - [Obsolete("Use the Instance static field instead of creating new instances")] - public PascalCaseNamingConvention() { } - - public string Apply(string value) - { - return value.ToPascalCase(); - } - -#pragma warning disable CS0618 // Type or member is obsolete - public static readonly INamingConvention Instance = new PascalCaseNamingConvention(); -#pragma warning restore CS0618 // Type or member is obsolete - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NamingConventions +{ + /// + /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to + /// pascal case (ThisIsATest). Pascal case is the same as camel case, except the first letter + /// is uppercase. + /// + public sealed class PascalCaseNamingConvention : INamingConvention + { + [Obsolete("Use the Instance static field instead of creating new instances")] + public PascalCaseNamingConvention() { } + + public string Apply(string value) + { + return value.ToPascalCase(); + } + +#pragma warning disable CS0618 // Type or member is obsolete + public static readonly INamingConvention Instance = new PascalCaseNamingConvention(); +#pragma warning restore CS0618 // Type or member is obsolete + } +} diff --git a/YamlDotNet/Serialization/NamingConventions/UnderscoredNamingConvention.cs b/YamlDotNet/Serialization/NamingConventions/UnderscoredNamingConvention.cs index d89ff97cf..28811e7d4 100644 --- a/YamlDotNet/Serialization/NamingConventions/UnderscoredNamingConvention.cs +++ b/YamlDotNet/Serialization/NamingConventions/UnderscoredNamingConvention.cs @@ -1,44 +1,44 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NamingConventions -{ - /// - /// Convert the string from camelcase (thisIsATest) to a underscored (this_is_a_test) string - /// - public sealed class UnderscoredNamingConvention : INamingConvention - { - [Obsolete("Use the Instance static field instead of creating new instances")] - public UnderscoredNamingConvention() { } - - public string Apply(string value) - { - return value.FromCamelCase("_"); - } - -#pragma warning disable CS0618 // Type or member is obsolete - public static readonly INamingConvention Instance = new UnderscoredNamingConvention(); -#pragma warning restore CS0618 // Type or member is obsolete - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NamingConventions +{ + /// + /// Convert the string from camelcase (thisIsATest) to a underscored (this_is_a_test) string + /// + public sealed class UnderscoredNamingConvention : INamingConvention + { + [Obsolete("Use the Instance static field instead of creating new instances")] + public UnderscoredNamingConvention() { } + + public string Apply(string value) + { + return value.FromCamelCase("_"); + } + +#pragma warning disable CS0618 // Type or member is obsolete + public static readonly INamingConvention Instance = new UnderscoredNamingConvention(); +#pragma warning restore CS0618 // Type or member is obsolete + } +} diff --git a/YamlDotNet/Serialization/NodeDeserializers/ArrayNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/ArrayNodeDeserializer.cs index 1e62c17fb..6a1d4d2ed 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/ArrayNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/ArrayNodeDeserializer.cs @@ -1,119 +1,119 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class ArrayNodeDeserializer : INodeDeserializer - { - bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { - if (!expectedType.IsArray) - { - value = false; - return false; - } - - var itemType = expectedType.GetElementType()!; // Arrays always have an element type - - var items = new ArrayList(); - CollectionNodeDeserializer.DeserializeHelper(itemType, parser, nestedObjectDeserializer, items, true); - - var array = Array.CreateInstance(itemType, items.Count); - items.CopyTo(array, 0); - - value = array; - return true; - } - - private sealed class ArrayList : IList - { - private object?[] data; - -#pragma warning disable CS8618 // Non-nullable field is uninitialized. Initialized inside Clear() - public ArrayList() -#pragma warning restore CS8618 // Non-nullable field is uninitialized. - { - Clear(); - } - - public int Add(object? value) - { - if (Count == data.Length) - { - Array.Resize(ref data, data.Length * 2); - } - data[Count] = value; - return Count++; - } - - public void Clear() - { - data = new object[10]; - Count = 0; - } - - bool IList.Contains(object? value) => throw new NotSupportedException(); - int IList.IndexOf(object? value) => throw new NotSupportedException(); - void IList.Insert(int index, object? value) => throw new NotSupportedException(); - void IList.Remove(object? value) => throw new NotSupportedException(); - void IList.RemoveAt(int index) => throw new NotSupportedException(); - - public bool IsFixedSize => false; - - public bool IsReadOnly => false; - - public object? this[int index] - { - get - { - return data[index]; - } - set - { - data[index] = value; - } - } - - public void CopyTo(Array array, int index) - { - Array.Copy(data, 0, array, index, Count); - } - - public int Count { get; private set; } - - public bool IsSynchronized => false; - public object SyncRoot => data; - - public IEnumerator GetEnumerator() - { - for (var i = 0; i < Count; ++i) - { - yield return data[i]; - } - } - } - } -} - +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class ArrayNodeDeserializer : INodeDeserializer + { + bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { + if (!expectedType.IsArray) + { + value = false; + return false; + } + + var itemType = expectedType.GetElementType()!; // Arrays always have an element type + + var items = new ArrayList(); + CollectionNodeDeserializer.DeserializeHelper(itemType, parser, nestedObjectDeserializer, items, true); + + var array = Array.CreateInstance(itemType, items.Count); + items.CopyTo(array, 0); + + value = array; + return true; + } + + private sealed class ArrayList : IList + { + private object?[] data; + +#pragma warning disable CS8618 // Non-nullable field is uninitialized. Initialized inside Clear() + public ArrayList() +#pragma warning restore CS8618 // Non-nullable field is uninitialized. + { + Clear(); + } + + public int Add(object? value) + { + if (Count == data.Length) + { + Array.Resize(ref data, data.Length * 2); + } + data[Count] = value; + return Count++; + } + + public void Clear() + { + data = new object[10]; + Count = 0; + } + + bool IList.Contains(object? value) => throw new NotSupportedException(); + int IList.IndexOf(object? value) => throw new NotSupportedException(); + void IList.Insert(int index, object? value) => throw new NotSupportedException(); + void IList.Remove(object? value) => throw new NotSupportedException(); + void IList.RemoveAt(int index) => throw new NotSupportedException(); + + public bool IsFixedSize => false; + + public bool IsReadOnly => false; + + public object? this[int index] + { + get + { + return data[index]; + } + set + { + data[index] = value; + } + } + + public void CopyTo(Array array, int index) + { + Array.Copy(data, 0, array, index, Count); + } + + public int Count { get; private set; } + + public bool IsSynchronized => false; + public object SyncRoot => data; + + public IEnumerator GetEnumerator() + { + for (var i = 0; i < Count; ++i) + { + yield return data[i]; + } + } + } + } +} + diff --git a/YamlDotNet/Serialization/NodeDeserializers/CollectionNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/CollectionNodeDeserializer.cs index 17206397b..dfc01df8f 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/CollectionNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/CollectionNodeDeserializer.cs @@ -1,111 +1,111 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Helpers; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class CollectionNodeDeserializer : INodeDeserializer - { - private readonly IObjectFactory objectFactory; - - public CollectionNodeDeserializer(IObjectFactory objectFactory) - { - this.objectFactory = objectFactory ?? throw new ArgumentNullException(nameof(objectFactory)); - } - - bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { - IList? list; - var canUpdate = true; - Type itemType; - var genericCollectionType = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(ICollection<>)); - if (genericCollectionType != null) - { - var genericArguments = genericCollectionType.GetGenericArguments(); - itemType = genericArguments[0]; - - value = objectFactory.Create(expectedType); - list = value as IList; - if (list == null) - { - // Uncommon case where a type implements IList but not IList - var genericListType = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IList<>)); - canUpdate = genericListType != null; - list = (IList?)Activator.CreateInstance(typeof(GenericCollectionToNonGenericAdapter<>).MakeGenericType(itemType), value); - } - } - else if (typeof(IList).IsAssignableFrom(expectedType)) - { - itemType = typeof(object); - - value = objectFactory.Create(expectedType); - list = (IList)value; - } - else - { - value = null; - return false; - } - - DeserializeHelper(itemType, parser, nestedObjectDeserializer, list!, canUpdate); - - return true; - } - - internal static void DeserializeHelper(Type tItem, IParser parser, Func nestedObjectDeserializer, IList result, bool canUpdate) - { - parser.Consume(); - while (!parser.TryConsume(out var _)) - { - var current = parser.Current; - - var value = nestedObjectDeserializer(parser, tItem); - if (value is IValuePromise promise) - { - if (canUpdate) - { - var index = result.Add(tItem.IsValueType() ? Activator.CreateInstance(tItem) : null); - promise.ValueAvailable += v => result[index] = TypeConverter.ChangeType(v, tItem); - } - else - { - throw new ForwardAnchorNotSupportedException( - current?.Start ?? Mark.Empty, - current?.End ?? Mark.Empty, - "Forward alias references are not allowed because this type does not implement IList<>" - ); - } - } - else - { - result.Add(TypeConverter.ChangeType(value, tItem)); - } - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Helpers; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class CollectionNodeDeserializer : INodeDeserializer + { + private readonly IObjectFactory objectFactory; + + public CollectionNodeDeserializer(IObjectFactory objectFactory) + { + this.objectFactory = objectFactory ?? throw new ArgumentNullException(nameof(objectFactory)); + } + + bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { + IList? list; + var canUpdate = true; + Type itemType; + var genericCollectionType = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(ICollection<>)); + if (genericCollectionType != null) + { + var genericArguments = genericCollectionType.GetGenericArguments(); + itemType = genericArguments[0]; + + value = objectFactory.Create(expectedType); + list = value as IList; + if (list == null) + { + // Uncommon case where a type implements IList but not IList + var genericListType = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IList<>)); + canUpdate = genericListType != null; + list = (IList?)Activator.CreateInstance(typeof(GenericCollectionToNonGenericAdapter<>).MakeGenericType(itemType), value); + } + } + else if (typeof(IList).IsAssignableFrom(expectedType)) + { + itemType = typeof(object); + + value = objectFactory.Create(expectedType); + list = (IList)value; + } + else + { + value = null; + return false; + } + + DeserializeHelper(itemType, parser, nestedObjectDeserializer, list!, canUpdate); + + return true; + } + + internal static void DeserializeHelper(Type tItem, IParser parser, Func nestedObjectDeserializer, IList result, bool canUpdate) + { + parser.Consume(); + while (!parser.TryConsume(out var _)) + { + var current = parser.Current; + + var value = nestedObjectDeserializer(parser, tItem); + if (value is IValuePromise promise) + { + if (canUpdate) + { + var index = result.Add(tItem.IsValueType() ? Activator.CreateInstance(tItem) : null); + promise.ValueAvailable += v => result[index] = TypeConverter.ChangeType(v, tItem); + } + else + { + throw new ForwardAnchorNotSupportedException( + current?.Start ?? Mark.Empty, + current?.End ?? Mark.Empty, + "Forward alias references are not allowed because this type does not implement IList<>" + ); + } + } + else + { + result.Add(TypeConverter.ChangeType(value, tItem)); + } + } + } + } +} diff --git a/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs index a277b49c5..3ec641424 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs @@ -1,149 +1,149 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Helpers; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class DictionaryNodeDeserializer : INodeDeserializer - { - private readonly IObjectFactory objectFactory; - - public DictionaryNodeDeserializer(IObjectFactory objectFactory) - { - this.objectFactory = objectFactory ?? throw new ArgumentNullException(nameof(objectFactory)); - } - - bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { - IDictionary? dictionary; - Type keyType, valueType; - var genericDictionaryType = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IDictionary<,>)); - if (genericDictionaryType != null) - { - var genericArguments = genericDictionaryType.GetGenericArguments(); - keyType = genericArguments[0]; - valueType = genericArguments[1]; - - value = objectFactory.Create(expectedType); - - dictionary = value as IDictionary; - if (dictionary == null) - { - // Uncommon case where a type implements IDictionary but not IDictionary - dictionary = (IDictionary?)Activator.CreateInstance(typeof(GenericDictionaryToNonGenericAdapter<,>).MakeGenericType(keyType, valueType), value); - } - } - else if (typeof(IDictionary).IsAssignableFrom(expectedType)) - { - keyType = typeof(object); - valueType = typeof(object); - - value = objectFactory.Create(expectedType); - dictionary = (IDictionary)value; - } - else - { - value = null; - return false; - } - - DeserializeHelper(keyType, valueType, parser, nestedObjectDeserializer, dictionary!); - - return true; - } - - private static void DeserializeHelper(Type tKey, Type tValue, IParser parser, Func nestedObjectDeserializer, IDictionary result) - { - parser.Consume(); - while (!parser.TryConsume(out var _)) - { - var key = nestedObjectDeserializer(parser, tKey); - var value = nestedObjectDeserializer(parser, tValue); - var valuePromise = value as IValuePromise; - - if (key is IValuePromise keyPromise) - { - if (valuePromise == null) - { - // Key is pending, value is known - keyPromise.ValueAvailable += v => result[v!] = value!; - } - else - { - // Both key and value are pending. We need to wait until both of them become available. - var hasFirstPart = false; - - keyPromise.ValueAvailable += v => - { - if (hasFirstPart) - { - result[v!] = value!; - } - else - { - key = v!; - hasFirstPart = true; - } - }; - - valuePromise.ValueAvailable += v => - { - if (hasFirstPart) - { - result[key] = v!; - } - else - { - value = v; - hasFirstPart = true; - } - }; - } - } - else - { - if (key == null) - { - throw new ArgumentException("Empty key names are not supported yet.", "key"); - } - - if (valuePromise == null) - { - // Happy path: both key and value are known - result[key] = value!; - } - else - { - // Key is known, value is pending - valuePromise.ValueAvailable += v => result[key!] = v!; - } - } - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Helpers; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class DictionaryNodeDeserializer : INodeDeserializer + { + private readonly IObjectFactory objectFactory; + + public DictionaryNodeDeserializer(IObjectFactory objectFactory) + { + this.objectFactory = objectFactory ?? throw new ArgumentNullException(nameof(objectFactory)); + } + + bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { + IDictionary? dictionary; + Type keyType, valueType; + var genericDictionaryType = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IDictionary<,>)); + if (genericDictionaryType != null) + { + var genericArguments = genericDictionaryType.GetGenericArguments(); + keyType = genericArguments[0]; + valueType = genericArguments[1]; + + value = objectFactory.Create(expectedType); + + dictionary = value as IDictionary; + if (dictionary == null) + { + // Uncommon case where a type implements IDictionary but not IDictionary + dictionary = (IDictionary?)Activator.CreateInstance(typeof(GenericDictionaryToNonGenericAdapter<,>).MakeGenericType(keyType, valueType), value); + } + } + else if (typeof(IDictionary).IsAssignableFrom(expectedType)) + { + keyType = typeof(object); + valueType = typeof(object); + + value = objectFactory.Create(expectedType); + dictionary = (IDictionary)value; + } + else + { + value = null; + return false; + } + + DeserializeHelper(keyType, valueType, parser, nestedObjectDeserializer, dictionary!); + + return true; + } + + private static void DeserializeHelper(Type tKey, Type tValue, IParser parser, Func nestedObjectDeserializer, IDictionary result) + { + parser.Consume(); + while (!parser.TryConsume(out var _)) + { + var key = nestedObjectDeserializer(parser, tKey); + var value = nestedObjectDeserializer(parser, tValue); + var valuePromise = value as IValuePromise; + + if (key is IValuePromise keyPromise) + { + if (valuePromise == null) + { + // Key is pending, value is known + keyPromise.ValueAvailable += v => result[v!] = value!; + } + else + { + // Both key and value are pending. We need to wait until both of them become available. + var hasFirstPart = false; + + keyPromise.ValueAvailable += v => + { + if (hasFirstPart) + { + result[v!] = value!; + } + else + { + key = v!; + hasFirstPart = true; + } + }; + + valuePromise.ValueAvailable += v => + { + if (hasFirstPart) + { + result[key] = v!; + } + else + { + value = v; + hasFirstPart = true; + } + }; + } + } + else + { + if (key == null) + { + throw new ArgumentException("Empty key names are not supported yet.", "key"); + } + + if (valuePromise == null) + { + // Happy path: both key and value are known + result[key] = value!; + } + else + { + // Key is known, value is pending + valuePromise.ValueAvailable += v => result[key!] = v!; + } + } + } + } + } +} diff --git a/YamlDotNet/Serialization/NodeDeserializers/EnumerableNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/EnumerableNodeDeserializer.cs index aed879d8e..c76eba998 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/EnumerableNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/EnumerableNodeDeserializer.cs @@ -1,57 +1,57 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class EnumerableNodeDeserializer : INodeDeserializer - { - bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { - Type itemsType; - if (expectedType == typeof(IEnumerable)) - { - itemsType = typeof(object); - } - else - { - var iEnumerable = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IEnumerable<>)); - if (iEnumerable != expectedType) - { - value = null; - return false; - } - - itemsType = iEnumerable.GetGenericArguments()[0]; - } - - var collectionType = typeof(List<>).MakeGenericType(itemsType); - value = nestedObjectDeserializer(parser, collectionType); - return true; - } - } -} - +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class EnumerableNodeDeserializer : INodeDeserializer + { + bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { + Type itemsType; + if (expectedType == typeof(IEnumerable)) + { + itemsType = typeof(object); + } + else + { + var iEnumerable = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IEnumerable<>)); + if (iEnumerable != expectedType) + { + value = null; + return false; + } + + itemsType = iEnumerable.GetGenericArguments()[0]; + } + + var collectionType = typeof(List<>).MakeGenericType(itemsType); + value = nestedObjectDeserializer(parser, collectionType); + return true; + } + } +} + diff --git a/YamlDotNet/Serialization/NodeDeserializers/NullNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/NullNodeDeserializer.cs index 2c7bb3717..9d079c41e 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/NullNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/NullNodeDeserializer.cs @@ -1,63 +1,63 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class NullNodeDeserializer : INodeDeserializer - { - bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { - value = null; - if (parser.Accept(out var evt)) - { - if (NodeIsNull(evt)) - { - parser.SkipThisAndNestedEvents(); - return true; - } - } - - return false; - } - - private bool NodeIsNull(NodeEvent nodeEvent) - { - // http://yaml.org/type/null.html - - if (nodeEvent.Tag == "tag:yaml.org,2002:null") - { - return true; - } - - if (nodeEvent is Scalar scalar && scalar.Style == Core.ScalarStyle.Plain && !scalar.IsKey) - { - var value = scalar.Value; - return value == "" || value == "~" || value == "null" || value == "Null" || value == "NULL"; - } - - return false; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class NullNodeDeserializer : INodeDeserializer + { + bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { + value = null; + if (parser.Accept(out var evt)) + { + if (NodeIsNull(evt)) + { + parser.SkipThisAndNestedEvents(); + return true; + } + } + + return false; + } + + private bool NodeIsNull(NodeEvent nodeEvent) + { + // http://yaml.org/type/null.html + + if (nodeEvent.Tag == "tag:yaml.org,2002:null") + { + return true; + } + + if (nodeEvent is Scalar scalar && scalar.Style == Core.ScalarStyle.Plain && !scalar.IsKey) + { + var value = scalar.Value; + return value == "" || value == "~" || value == "null" || value == "Null" || value == "NULL"; + } + + return false; + } + } +} diff --git a/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs index a401abf16..f7b40475d 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs @@ -1,100 +1,100 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Runtime.Serialization; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class ObjectNodeDeserializer : INodeDeserializer - { - private readonly IObjectFactory objectFactory; - private readonly ITypeInspector typeDescriptor; - private readonly bool ignoreUnmatched; - - public ObjectNodeDeserializer(IObjectFactory objectFactory, ITypeInspector typeDescriptor, bool ignoreUnmatched) - { - this.objectFactory = objectFactory ?? throw new ArgumentNullException(nameof(objectFactory)); - this.typeDescriptor = typeDescriptor ?? throw new ArgumentNullException(nameof(typeDescriptor)); - this.ignoreUnmatched = ignoreUnmatched; - } - - bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { - if (!parser.TryConsume(out var mapping)) - { - value = null; - return false; - } - - // Strip off the nullable type, if present. This is needed for nullable structs. - var implementationType = Nullable.GetUnderlyingType(expectedType) ?? expectedType; - - value = objectFactory.Create(implementationType); - while (!parser.TryConsume(out var _)) - { - var propertyName = parser.Consume(); - try - { - var property = typeDescriptor.GetProperty(implementationType, null, propertyName.Value, ignoreUnmatched); - if (property == null) - { - parser.SkipThisAndNestedEvents(); - continue; - } - - var propertyValue = nestedObjectDeserializer(parser, property.Type); - if (propertyValue is IValuePromise propertyValuePromise) - { - var valueRef = value; - propertyValuePromise.ValueAvailable += v => - { - var convertedValue = TypeConverter.ChangeType(v, property.Type); - property.Write(valueRef, convertedValue); - }; - } - else - { - var convertedValue = TypeConverter.ChangeType(propertyValue, property.Type); - property.Write(value, convertedValue); - } - } - catch (SerializationException ex) - { - throw new YamlException(propertyName.Start, propertyName.End, ex.Message); - } - catch (YamlException) - { - throw; - } - catch (Exception ex) - { - throw new YamlException(propertyName.Start, propertyName.End, "Exception during deserialization", ex); - } - } - - return true; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Runtime.Serialization; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class ObjectNodeDeserializer : INodeDeserializer + { + private readonly IObjectFactory objectFactory; + private readonly ITypeInspector typeDescriptor; + private readonly bool ignoreUnmatched; + + public ObjectNodeDeserializer(IObjectFactory objectFactory, ITypeInspector typeDescriptor, bool ignoreUnmatched) + { + this.objectFactory = objectFactory ?? throw new ArgumentNullException(nameof(objectFactory)); + this.typeDescriptor = typeDescriptor ?? throw new ArgumentNullException(nameof(typeDescriptor)); + this.ignoreUnmatched = ignoreUnmatched; + } + + bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { + if (!parser.TryConsume(out var mapping)) + { + value = null; + return false; + } + + // Strip off the nullable type, if present. This is needed for nullable structs. + var implementationType = Nullable.GetUnderlyingType(expectedType) ?? expectedType; + + value = objectFactory.Create(implementationType); + while (!parser.TryConsume(out var _)) + { + var propertyName = parser.Consume(); + try + { + var property = typeDescriptor.GetProperty(implementationType, null, propertyName.Value, ignoreUnmatched); + if (property == null) + { + parser.SkipThisAndNestedEvents(); + continue; + } + + var propertyValue = nestedObjectDeserializer(parser, property.Type); + if (propertyValue is IValuePromise propertyValuePromise) + { + var valueRef = value; + propertyValuePromise.ValueAvailable += v => + { + var convertedValue = TypeConverter.ChangeType(v, property.Type); + property.Write(valueRef, convertedValue); + }; + } + else + { + var convertedValue = TypeConverter.ChangeType(propertyValue, property.Type); + property.Write(value, convertedValue); + } + } + catch (SerializationException ex) + { + throw new YamlException(propertyName.Start, propertyName.End, ex.Message); + } + catch (YamlException) + { + throw; + } + catch (Exception ex) + { + throw new YamlException(propertyName.Start, propertyName.End, "Exception during deserialization", ex); + } + } + + return true; + } + } +} diff --git a/YamlDotNet/Serialization/NodeDeserializers/ScalarNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/ScalarNodeDeserializer.cs index 4c5cf91ec..b48bfa815 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/ScalarNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/ScalarNodeDeserializer.cs @@ -1,424 +1,424 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Globalization; -using System.Text.RegularExpressions; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Helpers; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class ScalarNodeDeserializer : INodeDeserializer - { - private const string BooleanTruePattern = "^(true|y|yes|on)$"; - private const string BooleanFalsePattern = "^(false|n|no|off)$"; - private readonly bool attemptUnknownTypeDeserialization; - - public ScalarNodeDeserializer() - : this(false) - { - - } - - public ScalarNodeDeserializer(bool attemptUnknownTypeDeserialization) - { - this.attemptUnknownTypeDeserialization = attemptUnknownTypeDeserialization; - } - - bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { - if (!parser.TryConsume(out var scalar)) - { - value = null; - return false; - } - - // Strip off the nullable type, if present - var underlyingType = Nullable.GetUnderlyingType(expectedType) ?? expectedType; - - if (underlyingType.IsEnum()) - { - value = Enum.Parse(underlyingType, scalar.Value, true); - return true; - } - - var typeCode = underlyingType.GetTypeCode(); - switch (typeCode) - { - case TypeCode.Boolean: - value = DeserializeBooleanHelper(scalar.Value); - break; - - case TypeCode.Byte: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - case TypeCode.SByte: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.UInt64: - value = DeserializeIntegerHelper(typeCode, scalar.Value); - break; - - case TypeCode.Single: - value = float.Parse(scalar.Value, YamlFormatter.NumberFormat); - break; - - case TypeCode.Double: - value = double.Parse(scalar.Value, YamlFormatter.NumberFormat); - break; - - case TypeCode.Decimal: - value = decimal.Parse(scalar.Value, YamlFormatter.NumberFormat); - break; - - case TypeCode.String: - value = scalar.Value; - break; - - case TypeCode.Char: - value = scalar.Value[0]; - break; - - case TypeCode.DateTime: - // TODO: This is probably incorrect. Use the correct regular expression. - value = DateTime.Parse(scalar.Value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); - break; - - default: - if (expectedType == typeof(object)) - { - if (!scalar.IsKey && attemptUnknownTypeDeserialization) - { - value = AttemptUnknownTypeDeserialization(scalar); - } - else - { - // Default to string - value = scalar.Value; - } - } - else - { - value = TypeConverter.ChangeType(scalar.Value, expectedType); - } - break; - } - return true; - } - - private object DeserializeBooleanHelper(string value) - { - bool result; - - if (Regex.IsMatch(value, BooleanTruePattern, RegexOptions.IgnoreCase)) - { - result = true; - } - else if (Regex.IsMatch(value, BooleanFalsePattern, RegexOptions.IgnoreCase)) - { - result = false; - } - else - { - throw new FormatException($"The value \"{value}\" is not a valid YAML Boolean"); - } - - return result; - } - - private static object DeserializeIntegerHelper(TypeCode typeCode, string value) - { - using var numberBuilderStringBuilder = StringBuilderPool.Rent(); - var numberBuilder = numberBuilderStringBuilder.Builder; - var currentIndex = 0; - var isNegative = false; - int numberBase; - ulong result = 0; - - if (value[0] == '-') - { - currentIndex++; - isNegative = true; - } - - else if (value[0] == '+') - { - currentIndex++; - } - - if (value[currentIndex] == '0') - { - // Could be binary, octal, hex, decimal (0) - - // If there are no characters remaining, it's a decimal zero - if (currentIndex == value.Length - 1) - { - numberBase = 10; - result = 0; - } - - else - { - // Check the next character - currentIndex++; - - if (value[currentIndex] == 'b') - { - // Binary - numberBase = 2; - - currentIndex++; - } - - else if (value[currentIndex] == 'x') - { - // Hex - numberBase = 16; - - currentIndex++; - } - - else - { - // Octal - numberBase = 8; - } - } - - // Copy remaining digits to the number buffer (skip underscores) - while (currentIndex < value.Length) - { - if (value[currentIndex] != '_') - { - numberBuilder.Append(value[currentIndex]); - } - currentIndex++; - } - - // Parse the magnitude of the number - switch (numberBase) - { - case 2: - case 8: - // TODO: how to incorporate the numberFormat? - result = Convert.ToUInt64(numberBuilder.ToString(), numberBase); - break; - - case 16: - result = ulong.Parse(numberBuilder.ToString(), NumberStyles.HexNumber, YamlFormatter.NumberFormat); - break; - - case 10: - // Result is already zero - break; - } - } - - else - { - // Could be decimal or base 60 - var chunks = value.Substring(currentIndex).Split(':'); - result = 0; - - for (var chunkIndex = 0; chunkIndex < chunks.Length; chunkIndex++) - { - result *= 60; - - // TODO: verify that chunks after the first are non-negative and less than 60 - result += ulong.Parse(chunks[chunkIndex].Replace("_", "")); - } - } - - if (isNegative) - { - long toCast; - - // we do this because abs(long.minvalue) is 1 more than long.maxvalue. - if (result == 9223372036854775808) // abs(long.minvalue) => ulong - { - toCast = long.MinValue; - } - else - { - // this will throw if it's too big. - toCast = checked(-(long)result); - } - - return CastInteger(toCast, typeCode); - } - else - { - return CastInteger(result, typeCode); - } - } - - private static object CastInteger(long number, TypeCode typeCode) - { - checked - { - return typeCode switch - { - TypeCode.Byte => (byte)number, - TypeCode.Int16 => (short)number, - TypeCode.Int32 => (int)number, - TypeCode.Int64 => number, - TypeCode.SByte => (sbyte)number, - TypeCode.UInt16 => (ushort)number, - TypeCode.UInt32 => (uint)number, - TypeCode.UInt64 => (ulong)number, - _ => number, - }; - } - } - - private static object CastInteger(ulong number, TypeCode typeCode) - { - checked - { - return typeCode switch - { - TypeCode.Byte => (byte)number, - TypeCode.Int16 => (short)number, - TypeCode.Int32 => (int)number, - TypeCode.Int64 => (long)number, - TypeCode.SByte => (sbyte)number, - TypeCode.UInt16 => (ushort)number, - TypeCode.UInt32 => (uint)number, - TypeCode.UInt64 => number, - _ => number, - }; - } - } - - private static object? AttemptUnknownTypeDeserialization(Scalar value) - { - if (value.Style == ScalarStyle.SingleQuoted || - value.Style == ScalarStyle.DoubleQuoted || - value.Style == ScalarStyle.Folded) - { - return value.Value; - } - var v = value.Value; - object? result; - - switch (v) - { - case "": - case "~": - case "null": - case "Null": - case "NULL": - return null; - case "true": - case "True": - case "TRUE": - return true; - case "false": - case "False": - case "FALSE": - return false; - default: - if (Regex.IsMatch(v, "0x[0-9a-fA-F]+")) //base16 number - { - if (TryAndSwallow(() => Convert.ToByte(v, 16), out result)) { } - else if (TryAndSwallow(() => Convert.ToInt16(v, 16), out result)) { } - else if (TryAndSwallow(() => Convert.ToInt32(v, 16), out result)) { } - else if (TryAndSwallow(() => Convert.ToInt64(v, 16), out result)) { } - else if (TryAndSwallow(() => Convert.ToUInt64(v, 16), out result)) { } - else - { - //we couldn't parse it, default to a string. It's probably to big. - result = v; - } - } - else if (Regex.IsMatch(v, "0o[0-9a-fA-F]+")) //base8 number - { - if (TryAndSwallow(() => Convert.ToByte(v, 8), out result)) { } - else if (TryAndSwallow(() => Convert.ToInt16(v, 8), out result)) { } - else if (TryAndSwallow(() => Convert.ToInt32(v, 8), out result)) { } - else if (TryAndSwallow(() => Convert.ToInt64(v, 8), out result)) { } - else if (TryAndSwallow(() => Convert.ToUInt64(v, 8), out result)) { } - else - { - //we couldn't parse it, default to a string. It's probably to big. - result = v; - } - } - else if (Regex.IsMatch(v, @"[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?")) //regular number - { - if (TryAndSwallow(() => byte.Parse(v), out result)) { } - else if (TryAndSwallow(() => short.Parse(v), out result)) { } - else if (TryAndSwallow(() => int.Parse(v), out result)) { } - else if (TryAndSwallow(() => long.Parse(v), out result)) { } - else if (TryAndSwallow(() => ulong.Parse(v), out result)) { } - else if (TryAndSwallow(() => float.Parse(v), out result)) { } - else if (TryAndSwallow(() => double.Parse(v), out result)) { } - else - { - //we couldn't parse it, default to string, It's probably too big - result = v; - } - } - else if (Regex.IsMatch(v, @"[-+]?(\.inf|\.Inf|\.INF)")) //infinities - { - if (v.StartsWith("-")) - { - result = float.NegativeInfinity; - } - else - { - result = float.PositiveInfinity; - } - } - else if (Regex.IsMatch(v, @"\.nan|\.NaN|\.NAN")) //not a number - { - result = float.NaN; - } - else - { - // not a known type, so make it a string. - result = v; - } - break; - } - - return result; - } - - private static bool TryAndSwallow(Func attempt, out object? value) - { - try - { - value = attempt(); - return true; - } - catch - { - value = null; - return false; - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Globalization; +using System.Text.RegularExpressions; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Helpers; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class ScalarNodeDeserializer : INodeDeserializer + { + private const string BooleanTruePattern = "^(true|y|yes|on)$"; + private const string BooleanFalsePattern = "^(false|n|no|off)$"; + private readonly bool attemptUnknownTypeDeserialization; + + public ScalarNodeDeserializer() + : this(false) + { + + } + + public ScalarNodeDeserializer(bool attemptUnknownTypeDeserialization) + { + this.attemptUnknownTypeDeserialization = attemptUnknownTypeDeserialization; + } + + bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { + if (!parser.TryConsume(out var scalar)) + { + value = null; + return false; + } + + // Strip off the nullable type, if present + var underlyingType = Nullable.GetUnderlyingType(expectedType) ?? expectedType; + + if (underlyingType.IsEnum()) + { + value = Enum.Parse(underlyingType, scalar.Value, true); + return true; + } + + var typeCode = underlyingType.GetTypeCode(); + switch (typeCode) + { + case TypeCode.Boolean: + value = DeserializeBooleanHelper(scalar.Value); + break; + + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + value = DeserializeIntegerHelper(typeCode, scalar.Value); + break; + + case TypeCode.Single: + value = float.Parse(scalar.Value, YamlFormatter.NumberFormat); + break; + + case TypeCode.Double: + value = double.Parse(scalar.Value, YamlFormatter.NumberFormat); + break; + + case TypeCode.Decimal: + value = decimal.Parse(scalar.Value, YamlFormatter.NumberFormat); + break; + + case TypeCode.String: + value = scalar.Value; + break; + + case TypeCode.Char: + value = scalar.Value[0]; + break; + + case TypeCode.DateTime: + // TODO: This is probably incorrect. Use the correct regular expression. + value = DateTime.Parse(scalar.Value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); + break; + + default: + if (expectedType == typeof(object)) + { + if (!scalar.IsKey && attemptUnknownTypeDeserialization) + { + value = AttemptUnknownTypeDeserialization(scalar); + } + else + { + // Default to string + value = scalar.Value; + } + } + else + { + value = TypeConverter.ChangeType(scalar.Value, expectedType); + } + break; + } + return true; + } + + private object DeserializeBooleanHelper(string value) + { + bool result; + + if (Regex.IsMatch(value, BooleanTruePattern, RegexOptions.IgnoreCase)) + { + result = true; + } + else if (Regex.IsMatch(value, BooleanFalsePattern, RegexOptions.IgnoreCase)) + { + result = false; + } + else + { + throw new FormatException($"The value \"{value}\" is not a valid YAML Boolean"); + } + + return result; + } + + private static object DeserializeIntegerHelper(TypeCode typeCode, string value) + { + using var numberBuilderStringBuilder = StringBuilderPool.Rent(); + var numberBuilder = numberBuilderStringBuilder.Builder; + var currentIndex = 0; + var isNegative = false; + int numberBase; + ulong result = 0; + + if (value[0] == '-') + { + currentIndex++; + isNegative = true; + } + + else if (value[0] == '+') + { + currentIndex++; + } + + if (value[currentIndex] == '0') + { + // Could be binary, octal, hex, decimal (0) + + // If there are no characters remaining, it's a decimal zero + if (currentIndex == value.Length - 1) + { + numberBase = 10; + result = 0; + } + + else + { + // Check the next character + currentIndex++; + + if (value[currentIndex] == 'b') + { + // Binary + numberBase = 2; + + currentIndex++; + } + + else if (value[currentIndex] == 'x') + { + // Hex + numberBase = 16; + + currentIndex++; + } + + else + { + // Octal + numberBase = 8; + } + } + + // Copy remaining digits to the number buffer (skip underscores) + while (currentIndex < value.Length) + { + if (value[currentIndex] != '_') + { + numberBuilder.Append(value[currentIndex]); + } + currentIndex++; + } + + // Parse the magnitude of the number + switch (numberBase) + { + case 2: + case 8: + // TODO: how to incorporate the numberFormat? + result = Convert.ToUInt64(numberBuilder.ToString(), numberBase); + break; + + case 16: + result = ulong.Parse(numberBuilder.ToString(), NumberStyles.HexNumber, YamlFormatter.NumberFormat); + break; + + case 10: + // Result is already zero + break; + } + } + + else + { + // Could be decimal or base 60 + var chunks = value.Substring(currentIndex).Split(':'); + result = 0; + + for (var chunkIndex = 0; chunkIndex < chunks.Length; chunkIndex++) + { + result *= 60; + + // TODO: verify that chunks after the first are non-negative and less than 60 + result += ulong.Parse(chunks[chunkIndex].Replace("_", "")); + } + } + + if (isNegative) + { + long toCast; + + // we do this because abs(long.minvalue) is 1 more than long.maxvalue. + if (result == 9223372036854775808) // abs(long.minvalue) => ulong + { + toCast = long.MinValue; + } + else + { + // this will throw if it's too big. + toCast = checked(-(long)result); + } + + return CastInteger(toCast, typeCode); + } + else + { + return CastInteger(result, typeCode); + } + } + + private static object CastInteger(long number, TypeCode typeCode) + { + checked + { + return typeCode switch + { + TypeCode.Byte => (byte)number, + TypeCode.Int16 => (short)number, + TypeCode.Int32 => (int)number, + TypeCode.Int64 => number, + TypeCode.SByte => (sbyte)number, + TypeCode.UInt16 => (ushort)number, + TypeCode.UInt32 => (uint)number, + TypeCode.UInt64 => (ulong)number, + _ => number, + }; + } + } + + private static object CastInteger(ulong number, TypeCode typeCode) + { + checked + { + return typeCode switch + { + TypeCode.Byte => (byte)number, + TypeCode.Int16 => (short)number, + TypeCode.Int32 => (int)number, + TypeCode.Int64 => (long)number, + TypeCode.SByte => (sbyte)number, + TypeCode.UInt16 => (ushort)number, + TypeCode.UInt32 => (uint)number, + TypeCode.UInt64 => number, + _ => number, + }; + } + } + + private static object? AttemptUnknownTypeDeserialization(Scalar value) + { + if (value.Style == ScalarStyle.SingleQuoted || + value.Style == ScalarStyle.DoubleQuoted || + value.Style == ScalarStyle.Folded) + { + return value.Value; + } + var v = value.Value; + object? result; + + switch (v) + { + case "": + case "~": + case "null": + case "Null": + case "NULL": + return null; + case "true": + case "True": + case "TRUE": + return true; + case "false": + case "False": + case "FALSE": + return false; + default: + if (Regex.IsMatch(v, "0x[0-9a-fA-F]+")) //base16 number + { + if (TryAndSwallow(() => Convert.ToByte(v, 16), out result)) { } + else if (TryAndSwallow(() => Convert.ToInt16(v, 16), out result)) { } + else if (TryAndSwallow(() => Convert.ToInt32(v, 16), out result)) { } + else if (TryAndSwallow(() => Convert.ToInt64(v, 16), out result)) { } + else if (TryAndSwallow(() => Convert.ToUInt64(v, 16), out result)) { } + else + { + //we couldn't parse it, default to a string. It's probably to big. + result = v; + } + } + else if (Regex.IsMatch(v, "0o[0-9a-fA-F]+")) //base8 number + { + if (TryAndSwallow(() => Convert.ToByte(v, 8), out result)) { } + else if (TryAndSwallow(() => Convert.ToInt16(v, 8), out result)) { } + else if (TryAndSwallow(() => Convert.ToInt32(v, 8), out result)) { } + else if (TryAndSwallow(() => Convert.ToInt64(v, 8), out result)) { } + else if (TryAndSwallow(() => Convert.ToUInt64(v, 8), out result)) { } + else + { + //we couldn't parse it, default to a string. It's probably to big. + result = v; + } + } + else if (Regex.IsMatch(v, @"[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?")) //regular number + { + if (TryAndSwallow(() => byte.Parse(v), out result)) { } + else if (TryAndSwallow(() => short.Parse(v), out result)) { } + else if (TryAndSwallow(() => int.Parse(v), out result)) { } + else if (TryAndSwallow(() => long.Parse(v), out result)) { } + else if (TryAndSwallow(() => ulong.Parse(v), out result)) { } + else if (TryAndSwallow(() => float.Parse(v), out result)) { } + else if (TryAndSwallow(() => double.Parse(v), out result)) { } + else + { + //we couldn't parse it, default to string, It's probably too big + result = v; + } + } + else if (Regex.IsMatch(v, @"[-+]?(\.inf|\.Inf|\.INF)")) //infinities + { + if (v.StartsWith("-")) + { + result = float.NegativeInfinity; + } + else + { + result = float.PositiveInfinity; + } + } + else if (Regex.IsMatch(v, @"\.nan|\.NaN|\.NAN")) //not a number + { + result = float.NaN; + } + else + { + // not a known type, so make it a string. + result = v; + } + break; + } + + return result; + } + + private static bool TryAndSwallow(Func attempt, out object? value) + { + try + { + value = attempt(); + return true; + } + catch + { + value = null; + return false; + } + } + } +} diff --git a/YamlDotNet/Serialization/NodeDeserializers/TypeConverterNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/TypeConverterNodeDeserializer.cs index d00420229..9a5617b9e 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/TypeConverterNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/TypeConverterNodeDeserializer.cs @@ -1,52 +1,52 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class TypeConverterNodeDeserializer : INodeDeserializer - { - private readonly IEnumerable converters; - - public TypeConverterNodeDeserializer(IEnumerable converters) - { - this.converters = converters ?? throw new ArgumentNullException(nameof(converters)); - } - - bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { - var converter = converters.FirstOrDefault(c => c.Accepts(expectedType)); - if (converter == null) - { - value = null; - return false; - } - - value = converter.ReadYaml(parser, expectedType); - return true; - } - } -} - +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class TypeConverterNodeDeserializer : INodeDeserializer + { + private readonly IEnumerable converters; + + public TypeConverterNodeDeserializer(IEnumerable converters) + { + this.converters = converters ?? throw new ArgumentNullException(nameof(converters)); + } + + bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { + var converter = converters.FirstOrDefault(c => c.Accepts(expectedType)); + if (converter == null) + { + value = null; + return false; + } + + value = converter.ReadYaml(parser, expectedType); + return true; + } + } +} + diff --git a/YamlDotNet/Serialization/NodeDeserializers/YamlConvertibleNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/YamlConvertibleNodeDeserializer.cs index 22fe9a26a..da3d9e7f3 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/YamlConvertibleNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/YamlConvertibleNodeDeserializer.cs @@ -1,50 +1,50 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class YamlConvertibleNodeDeserializer : INodeDeserializer - { - private readonly IObjectFactory objectFactory; - - public YamlConvertibleNodeDeserializer(IObjectFactory objectFactory) - { - this.objectFactory = objectFactory; - } - - public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { - if (typeof(IYamlConvertible).IsAssignableFrom(expectedType)) - { - var convertible = (IYamlConvertible)objectFactory.Create(expectedType); - convertible.Read(parser, expectedType, type => nestedObjectDeserializer(parser, type)); - value = convertible; - return true; - } - - value = null; - return false; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class YamlConvertibleNodeDeserializer : INodeDeserializer + { + private readonly IObjectFactory objectFactory; + + public YamlConvertibleNodeDeserializer(IObjectFactory objectFactory) + { + this.objectFactory = objectFactory; + } + + public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { + if (typeof(IYamlConvertible).IsAssignableFrom(expectedType)) + { + var convertible = (IYamlConvertible)objectFactory.Create(expectedType); + convertible.Read(parser, expectedType, type => nestedObjectDeserializer(parser, type)); + value = convertible; + return true; + } + + value = null; + return false; + } + } +} diff --git a/YamlDotNet/Serialization/NodeDeserializers/YamlSerializableNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/YamlSerializableNodeDeserializer.cs index 44c17f66c..62d622b25 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/YamlSerializableNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/YamlSerializableNodeDeserializer.cs @@ -1,52 +1,52 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.NodeDeserializers -{ - public sealed class YamlSerializableNodeDeserializer : INodeDeserializer - { - private readonly IObjectFactory objectFactory; - - public YamlSerializableNodeDeserializer(IObjectFactory objectFactory) - { - this.objectFactory = objectFactory; - } - - public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) - { -#pragma warning disable 0618 // IYamlSerializable is obsolete - if (typeof(IYamlSerializable).IsAssignableFrom(expectedType)) - { - var serializable = (IYamlSerializable)objectFactory.Create(expectedType); - serializable.ReadYaml(parser); - value = serializable; - return true; - } -#pragma warning restore - - value = null; - return false; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.NodeDeserializers +{ + public sealed class YamlSerializableNodeDeserializer : INodeDeserializer + { + private readonly IObjectFactory objectFactory; + + public YamlSerializableNodeDeserializer(IObjectFactory objectFactory) + { + this.objectFactory = objectFactory; + } + + public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) + { +#pragma warning disable 0618 // IYamlSerializable is obsolete + if (typeof(IYamlSerializable).IsAssignableFrom(expectedType)) + { + var serializable = (IYamlSerializable)objectFactory.Create(expectedType); + serializable.ReadYaml(parser); + value = serializable; + return true; + } +#pragma warning restore + + value = null; + return false; + } + } +} diff --git a/YamlDotNet/Serialization/NodeTypeResolvers/DefaultContainersNodeTypeResolver.cs b/YamlDotNet/Serialization/NodeTypeResolvers/DefaultContainersNodeTypeResolver.cs index 41686d7c6..a407cc21b 100644 --- a/YamlDotNet/Serialization/NodeTypeResolvers/DefaultContainersNodeTypeResolver.cs +++ b/YamlDotNet/Serialization/NodeTypeResolvers/DefaultContainersNodeTypeResolver.cs @@ -1,49 +1,49 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.NodeTypeResolvers -{ - public sealed class DefaultContainersNodeTypeResolver : INodeTypeResolver - { - bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) - { - if (currentType == typeof(object)) - { - if (nodeEvent is SequenceStart) - { - currentType = typeof(List); - return true; - } - if (nodeEvent is MappingStart) - { - currentType = typeof(Dictionary); - return true; - } - } - - return false; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.NodeTypeResolvers +{ + public sealed class DefaultContainersNodeTypeResolver : INodeTypeResolver + { + bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) + { + if (currentType == typeof(object)) + { + if (nodeEvent is SequenceStart) + { + currentType = typeof(List); + return true; + } + if (nodeEvent is MappingStart) + { + currentType = typeof(Dictionary); + return true; + } + } + + return false; + } + } +} diff --git a/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs b/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs index 24d4eb229..5d974aa7c 100644 --- a/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs +++ b/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs @@ -1,61 +1,61 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.NodeTypeResolvers -{ - public class MappingNodeTypeResolver : INodeTypeResolver - { - private readonly IDictionary _mappings; - - public MappingNodeTypeResolver(IDictionary mappings) - { - if (mappings == null) - { - throw new ArgumentNullException(nameof(mappings)); - } - - foreach (var pair in mappings) - { - if (!pair.Key.IsAssignableFrom(pair.Value)) - { - throw new InvalidOperationException($"Type '{pair.Value}' does not implement type '{pair.Key}'."); - } - } - - _mappings = mappings; - } - - public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) - { - if (_mappings.TryGetValue(currentType, out var concreteType)) - { - currentType = concreteType; - return true; - } - - return false; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.NodeTypeResolvers +{ + public class MappingNodeTypeResolver : INodeTypeResolver + { + private readonly IDictionary _mappings; + + public MappingNodeTypeResolver(IDictionary mappings) + { + if (mappings == null) + { + throw new ArgumentNullException(nameof(mappings)); + } + + foreach (var pair in mappings) + { + if (!pair.Key.IsAssignableFrom(pair.Value)) + { + throw new InvalidOperationException($"Type '{pair.Value}' does not implement type '{pair.Key}'."); + } + } + + _mappings = mappings; + } + + public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) + { + if (_mappings.TryGetValue(currentType, out var concreteType)) + { + currentType = concreteType; + return true; + } + + return false; + } + } +} diff --git a/YamlDotNet/Serialization/NodeTypeResolvers/RejectUnknownTagsNodeTypeResolver.cs b/YamlDotNet/Serialization/NodeTypeResolvers/RejectUnknownTagsNodeTypeResolver.cs index 0cc20b3a1..6d9637bb8 100644 --- a/YamlDotNet/Serialization/NodeTypeResolvers/RejectUnknownTagsNodeTypeResolver.cs +++ b/YamlDotNet/Serialization/NodeTypeResolvers/RejectUnknownTagsNodeTypeResolver.cs @@ -1,39 +1,39 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.NodeTypeResolvers -{ - public class PreventUnknownTagsNodeTypeResolver : INodeTypeResolver - { - bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) - { - if (nodeEvent != null && !nodeEvent.Tag.IsEmpty) - { - throw new YamlException(nodeEvent.Start, nodeEvent.End, $"Encountered an unresolved tag '{nodeEvent.Tag}'"); - } - return false; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.NodeTypeResolvers +{ + public class PreventUnknownTagsNodeTypeResolver : INodeTypeResolver + { + bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) + { + if (nodeEvent != null && !nodeEvent.Tag.IsEmpty) + { + throw new YamlException(nodeEvent.Start, nodeEvent.End, $"Encountered an unresolved tag '{nodeEvent.Tag}'"); + } + return false; + } + } +} diff --git a/YamlDotNet/Serialization/NodeTypeResolvers/TagNodeTypeResolver.cs b/YamlDotNet/Serialization/NodeTypeResolvers/TagNodeTypeResolver.cs index 7e4bae6fe..0852c433d 100644 --- a/YamlDotNet/Serialization/NodeTypeResolvers/TagNodeTypeResolver.cs +++ b/YamlDotNet/Serialization/NodeTypeResolvers/TagNodeTypeResolver.cs @@ -1,48 +1,48 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.NodeTypeResolvers -{ - public sealed class TagNodeTypeResolver : INodeTypeResolver - { - private readonly IDictionary tagMappings; - - public TagNodeTypeResolver(IDictionary tagMappings) - { - this.tagMappings = tagMappings ?? throw new ArgumentNullException(nameof(tagMappings)); - } - - bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) - { - if (nodeEvent != null && !nodeEvent.Tag.IsEmpty && tagMappings.TryGetValue(nodeEvent.Tag, out var predefinedType)) - { - currentType = predefinedType; - return true; - } - return false; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.NodeTypeResolvers +{ + public sealed class TagNodeTypeResolver : INodeTypeResolver + { + private readonly IDictionary tagMappings; + + public TagNodeTypeResolver(IDictionary tagMappings) + { + this.tagMappings = tagMappings ?? throw new ArgumentNullException(nameof(tagMappings)); + } + + bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) + { + if (nodeEvent != null && !nodeEvent.Tag.IsEmpty && tagMappings.TryGetValue(nodeEvent.Tag, out var predefinedType)) + { + currentType = predefinedType; + return true; + } + return false; + } + } +} diff --git a/YamlDotNet/Serialization/NodeTypeResolvers/TypeNameInTagNodeTypeResolver.cs b/YamlDotNet/Serialization/NodeTypeResolvers/TypeNameInTagNodeTypeResolver.cs index f922f92bd..348dcc525 100644 --- a/YamlDotNet/Serialization/NodeTypeResolvers/TypeNameInTagNodeTypeResolver.cs +++ b/YamlDotNet/Serialization/NodeTypeResolvers/TypeNameInTagNodeTypeResolver.cs @@ -1,46 +1,46 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.NodeTypeResolvers -{ - [Obsolete("The mechanism that this class uses to specify type names is non-standard. Register the tags explicitly instead of using this convention.")] - public sealed class TypeNameInTagNodeTypeResolver : INodeTypeResolver - { - bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) - { - if (nodeEvent != null && !nodeEvent.Tag.IsEmpty) - { - // If type could not be loaded, make sure to pass resolving - // to the next resolver - var resolvedType = Type.GetType(nodeEvent.Tag.Value.Substring(1), throwOnError: false); - if (resolvedType != null) - { - currentType = resolvedType; - return true; - } - } - return false; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.NodeTypeResolvers +{ + [Obsolete("The mechanism that this class uses to specify type names is non-standard. Register the tags explicitly instead of using this convention.")] + public sealed class TypeNameInTagNodeTypeResolver : INodeTypeResolver + { + bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) + { + if (nodeEvent != null && !nodeEvent.Tag.IsEmpty) + { + // If type could not be loaded, make sure to pass resolving + // to the next resolver + var resolvedType = Type.GetType(nodeEvent.Tag.Value.Substring(1), throwOnError: false); + if (resolvedType != null) + { + currentType = resolvedType; + return true; + } + } + return false; + } + } +} diff --git a/YamlDotNet/Serialization/NodeTypeResolvers/YamlConvertibleTypeResolver.cs b/YamlDotNet/Serialization/NodeTypeResolvers/YamlConvertibleTypeResolver.cs index 2acbda232..f0c77b39f 100644 --- a/YamlDotNet/Serialization/NodeTypeResolvers/YamlConvertibleTypeResolver.cs +++ b/YamlDotNet/Serialization/NodeTypeResolvers/YamlConvertibleTypeResolver.cs @@ -1,34 +1,34 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.NodeTypeResolvers -{ - public sealed class YamlConvertibleTypeResolver : INodeTypeResolver - { - public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) - { - return typeof(IYamlConvertible).IsAssignableFrom(currentType); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.NodeTypeResolvers +{ + public sealed class YamlConvertibleTypeResolver : INodeTypeResolver + { + public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) + { + return typeof(IYamlConvertible).IsAssignableFrom(currentType); + } + } +} diff --git a/YamlDotNet/Serialization/NodeTypeResolvers/YamlSerializableTypeResolver.cs b/YamlDotNet/Serialization/NodeTypeResolvers/YamlSerializableTypeResolver.cs index 59162f683..6756545da 100644 --- a/YamlDotNet/Serialization/NodeTypeResolvers/YamlSerializableTypeResolver.cs +++ b/YamlDotNet/Serialization/NodeTypeResolvers/YamlSerializableTypeResolver.cs @@ -1,36 +1,36 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization.NodeTypeResolvers -{ - public sealed class YamlSerializableTypeResolver : INodeTypeResolver - { - public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) - { -#pragma warning disable 0618 // IYamlSerializable is obsolete - return typeof(IYamlSerializable).IsAssignableFrom(currentType); -#pragma warning restore 0618 - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.NodeTypeResolvers +{ + public sealed class YamlSerializableTypeResolver : INodeTypeResolver + { + public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) + { +#pragma warning disable 0618 // IYamlSerializable is obsolete + return typeof(IYamlSerializable).IsAssignableFrom(currentType); +#pragma warning restore 0618 + } + } +} diff --git a/YamlDotNet/Serialization/Nothing.cs b/YamlDotNet/Serialization/Nothing.cs index 152b6ba91..ecc78c8ed 100644 --- a/YamlDotNet/Serialization/Nothing.cs +++ b/YamlDotNet/Serialization/Nothing.cs @@ -1,31 +1,31 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - - -namespace YamlDotNet.Serialization -{ - /// - /// An empty type for cases where a type needs to be provided but won't be used. - /// - public struct Nothing - { - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + + +namespace YamlDotNet.Serialization +{ + /// + /// An empty type for cases where a type needs to be provided but won't be used. + /// + public struct Nothing + { + } +} diff --git a/YamlDotNet/Serialization/ObjectDescriptor.cs b/YamlDotNet/Serialization/ObjectDescriptor.cs index ce4b6e6e4..500f07dd4 100644 --- a/YamlDotNet/Serialization/ObjectDescriptor.cs +++ b/YamlDotNet/Serialization/ObjectDescriptor.cs @@ -1,48 +1,48 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public sealed class ObjectDescriptor : IObjectDescriptor - { - public object? Value { get; private set; } - public Type Type { get; private set; } - public Type StaticType { get; private set; } - public ScalarStyle ScalarStyle { get; private set; } - - public ObjectDescriptor(object? value, Type type, Type staticType) - : this(value, type, staticType, ScalarStyle.Any) - { - } - - public ObjectDescriptor(object? value, Type type, Type staticType, ScalarStyle scalarStyle) - { - Value = value; - Type = type ?? throw new ArgumentNullException(nameof(type)); - StaticType = staticType ?? throw new ArgumentNullException(nameof(staticType)); - - ScalarStyle = scalarStyle; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public sealed class ObjectDescriptor : IObjectDescriptor + { + public object? Value { get; private set; } + public Type Type { get; private set; } + public Type StaticType { get; private set; } + public ScalarStyle ScalarStyle { get; private set; } + + public ObjectDescriptor(object? value, Type type, Type staticType) + : this(value, type, staticType, ScalarStyle.Any) + { + } + + public ObjectDescriptor(object? value, Type type, Type staticType, ScalarStyle scalarStyle) + { + Value = value; + Type = type ?? throw new ArgumentNullException(nameof(type)); + StaticType = staticType ?? throw new ArgumentNullException(nameof(staticType)); + + ScalarStyle = scalarStyle; + } + } +} diff --git a/YamlDotNet/Serialization/ObjectFactories/DefaultObjectFactory.cs b/YamlDotNet/Serialization/ObjectFactories/DefaultObjectFactory.cs index 097fd4265..6b906ba96 100644 --- a/YamlDotNet/Serialization/ObjectFactories/DefaultObjectFactory.cs +++ b/YamlDotNet/Serialization/ObjectFactories/DefaultObjectFactory.cs @@ -1,97 +1,97 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace YamlDotNet.Serialization.ObjectFactories -{ - /// - /// Creates objects using Activator.CreateInstance. - /// - public sealed class DefaultObjectFactory : IObjectFactory - { - private readonly Dictionary DefaultGenericInterfaceImplementations = new Dictionary - { - { typeof(IEnumerable<>), typeof(List<>) }, - { typeof(ICollection<>), typeof(List<>) }, - { typeof(IList<>), typeof(List<>) }, - { typeof(IDictionary<,>), typeof(Dictionary<,>) } - }; - - private readonly Dictionary DefaultNonGenericInterfaceImplementations = new Dictionary - { - { typeof(IEnumerable), typeof(List) }, - { typeof(ICollection), typeof(List) }, - { typeof(IList), typeof(List) }, - { typeof(IDictionary), typeof(Dictionary) } - }; - - public DefaultObjectFactory() - { - } - - public DefaultObjectFactory(IDictionary mappings) - { - foreach (var pair in mappings) - { - if (!pair.Key.IsAssignableFrom(pair.Value)) - { - throw new InvalidOperationException($"Type '{pair.Value}' does not implement type '{pair.Key}'."); - } - - DefaultNonGenericInterfaceImplementations.Add(pair.Key, pair.Value); - } - } - - public object Create(Type type) - { - if (type.IsInterface()) - { - if (type.IsGenericType()) - { - if (DefaultGenericInterfaceImplementations.TryGetValue(type.GetGenericTypeDefinition(), out var implementationType)) - { - type = implementationType.MakeGenericType(type.GetGenericArguments()); - } - } - else - { - if (DefaultNonGenericInterfaceImplementations.TryGetValue(type, out var implementationType)) - { - type = implementationType; - } - } - } - - try - { - return Activator.CreateInstance(type)!; - } - catch (Exception err) - { - var message = $"Failed to create an instance of type '{type.FullName}'."; - throw new InvalidOperationException(message, err); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YamlDotNet.Serialization.ObjectFactories +{ + /// + /// Creates objects using Activator.CreateInstance. + /// + public sealed class DefaultObjectFactory : IObjectFactory + { + private readonly Dictionary DefaultGenericInterfaceImplementations = new Dictionary + { + { typeof(IEnumerable<>), typeof(List<>) }, + { typeof(ICollection<>), typeof(List<>) }, + { typeof(IList<>), typeof(List<>) }, + { typeof(IDictionary<,>), typeof(Dictionary<,>) } + }; + + private readonly Dictionary DefaultNonGenericInterfaceImplementations = new Dictionary + { + { typeof(IEnumerable), typeof(List) }, + { typeof(ICollection), typeof(List) }, + { typeof(IList), typeof(List) }, + { typeof(IDictionary), typeof(Dictionary) } + }; + + public DefaultObjectFactory() + { + } + + public DefaultObjectFactory(IDictionary mappings) + { + foreach (var pair in mappings) + { + if (!pair.Key.IsAssignableFrom(pair.Value)) + { + throw new InvalidOperationException($"Type '{pair.Value}' does not implement type '{pair.Key}'."); + } + + DefaultNonGenericInterfaceImplementations.Add(pair.Key, pair.Value); + } + } + + public object Create(Type type) + { + if (type.IsInterface()) + { + if (type.IsGenericType()) + { + if (DefaultGenericInterfaceImplementations.TryGetValue(type.GetGenericTypeDefinition(), out var implementationType)) + { + type = implementationType.MakeGenericType(type.GetGenericArguments()); + } + } + else + { + if (DefaultNonGenericInterfaceImplementations.TryGetValue(type, out var implementationType)) + { + type = implementationType; + } + } + } + + try + { + return Activator.CreateInstance(type)!; + } + catch (Exception err) + { + var message = $"Failed to create an instance of type '{type.FullName}'."; + throw new InvalidOperationException(message, err); + } + } + } +} diff --git a/YamlDotNet/Serialization/ObjectFactories/LambdaObjectFactory.cs b/YamlDotNet/Serialization/ObjectFactories/LambdaObjectFactory.cs index 702388e2e..a1fea46e2 100644 --- a/YamlDotNet/Serialization/ObjectFactories/LambdaObjectFactory.cs +++ b/YamlDotNet/Serialization/ObjectFactories/LambdaObjectFactory.cs @@ -1,43 +1,43 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Serialization.ObjectFactories -{ - /// - /// Creates objects using a Func{Type,object}"/>. - /// - public sealed class LambdaObjectFactory : IObjectFactory - { - private readonly Func factory; - - public LambdaObjectFactory(Func factory) - { - this.factory = factory ?? throw new ArgumentNullException(nameof(factory)); - } - - public object Create(Type type) - { - return factory(type); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Serialization.ObjectFactories +{ + /// + /// Creates objects using a Func{Type,object}"/>. + /// + public sealed class LambdaObjectFactory : IObjectFactory + { + private readonly Func factory; + + public LambdaObjectFactory(Func factory) + { + this.factory = factory ?? throw new ArgumentNullException(nameof(factory)); + } + + public object Create(Type type) + { + return factory(type); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphTraversalStrategies/FullObjectGraphTraversalStrategy.cs b/YamlDotNet/Serialization/ObjectGraphTraversalStrategies/FullObjectGraphTraversalStrategy.cs index 1e04185e9..132e9f1bc 100644 --- a/YamlDotNet/Serialization/ObjectGraphTraversalStrategies/FullObjectGraphTraversalStrategy.cs +++ b/YamlDotNet/Serialization/ObjectGraphTraversalStrategies/FullObjectGraphTraversalStrategy.cs @@ -1,257 +1,257 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; -using YamlDotNet.Core; -using YamlDotNet.Helpers; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.ObjectGraphTraversalStrategies -{ - /// - /// An implementation of that traverses - /// readable properties, collections and dictionaries. - /// - public class FullObjectGraphTraversalStrategy : IObjectGraphTraversalStrategy - { - private readonly int maxRecursion; - private readonly ITypeInspector typeDescriptor; - private readonly ITypeResolver typeResolver; - private readonly INamingConvention namingConvention; - - public FullObjectGraphTraversalStrategy(ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention namingConvention) - { - if (maxRecursion <= 0) - { - throw new ArgumentOutOfRangeException(nameof(maxRecursion), maxRecursion, "maxRecursion must be greater than 1"); - } - - this.typeDescriptor = typeDescriptor ?? throw new ArgumentNullException(nameof(typeDescriptor)); - this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); - - this.maxRecursion = maxRecursion; - this.namingConvention = namingConvention ?? throw new ArgumentNullException(nameof(namingConvention)); - } - - void IObjectGraphTraversalStrategy.Traverse(IObjectDescriptor graph, IObjectGraphVisitor visitor, TContext context) - { - Traverse("", graph, visitor, context, new Stack(maxRecursion)); - } - - protected struct ObjectPathSegment - { - public readonly object Name; - public readonly IObjectDescriptor Value; - - public ObjectPathSegment(object name, IObjectDescriptor value) - { - this.Name = name; - this.Value = value; - } - } - - protected virtual void Traverse(object name, IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) - { - if (path.Count >= maxRecursion) - { - var message = new StringBuilder(); - message.AppendLine("Too much recursion when traversing the object graph."); - message.AppendLine("The path to reach this recursion was:"); - - var lines = new Stack>(path.Count); - var maxNameLength = 0; - foreach (var segment in path) - { - var segmentName = TypeConverter.ChangeType(segment.Name); - maxNameLength = Math.Max(maxNameLength, segmentName.Length); - lines.Push(new KeyValuePair(segmentName, segment.Value.Type.FullName!)); - } - - foreach (var line in lines) - { - message - .Append(" -> ") - .Append(line.Key.PadRight(maxNameLength)) - .Append(" [") - .Append(line.Value) - .AppendLine("]"); - } - - throw new MaximumRecursionLevelReachedException(message.ToString()); - } - - if (!visitor.Enter(value, context)) - { - return; - } - - path.Push(new ObjectPathSegment(name, value)); - try - { - var typeCode = value.Type.GetTypeCode(); - switch (typeCode) - { - case TypeCode.Boolean: - case TypeCode.Byte: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - case TypeCode.SByte: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.UInt64: - case TypeCode.Single: - case TypeCode.Double: - case TypeCode.Decimal: - case TypeCode.String: - case TypeCode.Char: - case TypeCode.DateTime: - visitor.VisitScalar(value, context); - break; - - case TypeCode.Empty: - throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); - - default: - if (value.IsDbNull()) - { - visitor.VisitScalar(new ObjectDescriptor(null, typeof(object), typeof(object)), context); - } - - if (value.Value == null || value.Type == typeof(TimeSpan)) - { - visitor.VisitScalar(value, context); - break; - } - - var underlyingType = Nullable.GetUnderlyingType(value.Type); - if (underlyingType != null) - { - // This is a nullable type, recursively handle it with its underlying type. - // Note that if it contains null, the condition above already took care of it - Traverse("Value", new ObjectDescriptor(value.Value, underlyingType, value.Type, value.ScalarStyle), visitor, context, path); - } - else - { - TraverseObject(value, visitor, context, path); - } - break; - } - } - finally - { - path.Pop(); - } - } - - protected virtual void TraverseObject(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) - { - if (typeof(IDictionary).IsAssignableFrom(value.Type)) - { - TraverseDictionary(value, visitor, typeof(object), typeof(object), context, path); - return; - } - - var genericDictionaryType = ReflectionUtility.GetImplementedGenericInterface(value.Type, typeof(IDictionary<,>)); - if (genericDictionaryType != null) - { - var genericArguments = genericDictionaryType.GetGenericArguments(); - var adaptedDictionary = Activator.CreateInstance(typeof(GenericDictionaryToNonGenericAdapter<,>).MakeGenericType(genericArguments), value.Value)!; - TraverseDictionary(new ObjectDescriptor(adaptedDictionary, value.Type, value.StaticType, value.ScalarStyle), visitor, genericArguments[0], genericArguments[1], context, path); - return; - } - - if (typeof(IEnumerable).IsAssignableFrom(value.Type)) - { - TraverseList(value, visitor, context, path); - return; - } - - TraverseProperties(value, visitor, context, path); - } - - protected virtual void TraverseDictionary(IObjectDescriptor dictionary, IObjectGraphVisitor visitor, Type keyType, Type valueType, TContext context, Stack path) - { - visitor.VisitMappingStart(dictionary, keyType, valueType, context); - - var isDynamic = dictionary.Type.FullName!.Equals("System.Dynamic.ExpandoObject"); - foreach (DictionaryEntry? entry in (IDictionary)dictionary.NonNullValue()) - { - var entryValue = entry!.Value; - var keyValue = isDynamic ? namingConvention.Apply(entryValue.Key.ToString()!) : entryValue.Key; - var key = GetObjectDescriptor(keyValue, keyType); - var value = GetObjectDescriptor(entryValue.Value, valueType); - - if (visitor.EnterMapping(key, value, context)) - { - Traverse(keyValue, key, visitor, context, path); - Traverse(keyValue, value, visitor, context, path); - } - } - - visitor.VisitMappingEnd(dictionary, context); - } - - private void TraverseList(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) - { - var enumerableType = ReflectionUtility.GetImplementedGenericInterface(value.Type, typeof(IEnumerable<>)); - var itemType = enumerableType != null ? enumerableType.GetGenericArguments()[0] : typeof(object); - - visitor.VisitSequenceStart(value, itemType, context); - - var index = 0; - - foreach (var item in (IEnumerable)value.NonNullValue()) - { - Traverse(index, GetObjectDescriptor(item, itemType), visitor, context, path); - ++index; - } - - visitor.VisitSequenceEnd(value, context); - } - - protected virtual void TraverseProperties(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) - { - visitor.VisitMappingStart(value, typeof(string), typeof(object), context); - - var source = value.NonNullValue(); - foreach (var propertyDescriptor in typeDescriptor.GetProperties(value.Type, source)) - { - var propertyValue = propertyDescriptor.Read(source); - if (visitor.EnterMapping(propertyDescriptor, propertyValue, context)) - { - Traverse(propertyDescriptor.Name, new ObjectDescriptor(propertyDescriptor.Name, typeof(string), typeof(string), ScalarStyle.Plain), visitor, context, path); - Traverse(propertyDescriptor.Name, propertyValue, visitor, context, path); - } - } - - visitor.VisitMappingEnd(value, context); - } - - private IObjectDescriptor GetObjectDescriptor(object? value, Type staticType) - { - return new ObjectDescriptor(value, typeResolver.Resolve(staticType, value), staticType); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using YamlDotNet.Core; +using YamlDotNet.Helpers; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.ObjectGraphTraversalStrategies +{ + /// + /// An implementation of that traverses + /// readable properties, collections and dictionaries. + /// + public class FullObjectGraphTraversalStrategy : IObjectGraphTraversalStrategy + { + private readonly int maxRecursion; + private readonly ITypeInspector typeDescriptor; + private readonly ITypeResolver typeResolver; + private readonly INamingConvention namingConvention; + + public FullObjectGraphTraversalStrategy(ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention namingConvention) + { + if (maxRecursion <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxRecursion), maxRecursion, "maxRecursion must be greater than 1"); + } + + this.typeDescriptor = typeDescriptor ?? throw new ArgumentNullException(nameof(typeDescriptor)); + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); + + this.maxRecursion = maxRecursion; + this.namingConvention = namingConvention ?? throw new ArgumentNullException(nameof(namingConvention)); + } + + void IObjectGraphTraversalStrategy.Traverse(IObjectDescriptor graph, IObjectGraphVisitor visitor, TContext context) + { + Traverse("", graph, visitor, context, new Stack(maxRecursion)); + } + + protected struct ObjectPathSegment + { + public readonly object Name; + public readonly IObjectDescriptor Value; + + public ObjectPathSegment(object name, IObjectDescriptor value) + { + this.Name = name; + this.Value = value; + } + } + + protected virtual void Traverse(object name, IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) + { + if (path.Count >= maxRecursion) + { + var message = new StringBuilder(); + message.AppendLine("Too much recursion when traversing the object graph."); + message.AppendLine("The path to reach this recursion was:"); + + var lines = new Stack>(path.Count); + var maxNameLength = 0; + foreach (var segment in path) + { + var segmentName = TypeConverter.ChangeType(segment.Name); + maxNameLength = Math.Max(maxNameLength, segmentName.Length); + lines.Push(new KeyValuePair(segmentName, segment.Value.Type.FullName!)); + } + + foreach (var line in lines) + { + message + .Append(" -> ") + .Append(line.Key.PadRight(maxNameLength)) + .Append(" [") + .Append(line.Value) + .AppendLine("]"); + } + + throw new MaximumRecursionLevelReachedException(message.ToString()); + } + + if (!visitor.Enter(value, context)) + { + return; + } + + path.Push(new ObjectPathSegment(name, value)); + try + { + var typeCode = value.Type.GetTypeCode(); + switch (typeCode) + { + case TypeCode.Boolean: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + case TypeCode.String: + case TypeCode.Char: + case TypeCode.DateTime: + visitor.VisitScalar(value, context); + break; + + case TypeCode.Empty: + throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); + + default: + if (value.IsDbNull()) + { + visitor.VisitScalar(new ObjectDescriptor(null, typeof(object), typeof(object)), context); + } + + if (value.Value == null || value.Type == typeof(TimeSpan)) + { + visitor.VisitScalar(value, context); + break; + } + + var underlyingType = Nullable.GetUnderlyingType(value.Type); + if (underlyingType != null) + { + // This is a nullable type, recursively handle it with its underlying type. + // Note that if it contains null, the condition above already took care of it + Traverse("Value", new ObjectDescriptor(value.Value, underlyingType, value.Type, value.ScalarStyle), visitor, context, path); + } + else + { + TraverseObject(value, visitor, context, path); + } + break; + } + } + finally + { + path.Pop(); + } + } + + protected virtual void TraverseObject(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) + { + if (typeof(IDictionary).IsAssignableFrom(value.Type)) + { + TraverseDictionary(value, visitor, typeof(object), typeof(object), context, path); + return; + } + + var genericDictionaryType = ReflectionUtility.GetImplementedGenericInterface(value.Type, typeof(IDictionary<,>)); + if (genericDictionaryType != null) + { + var genericArguments = genericDictionaryType.GetGenericArguments(); + var adaptedDictionary = Activator.CreateInstance(typeof(GenericDictionaryToNonGenericAdapter<,>).MakeGenericType(genericArguments), value.Value)!; + TraverseDictionary(new ObjectDescriptor(adaptedDictionary, value.Type, value.StaticType, value.ScalarStyle), visitor, genericArguments[0], genericArguments[1], context, path); + return; + } + + if (typeof(IEnumerable).IsAssignableFrom(value.Type)) + { + TraverseList(value, visitor, context, path); + return; + } + + TraverseProperties(value, visitor, context, path); + } + + protected virtual void TraverseDictionary(IObjectDescriptor dictionary, IObjectGraphVisitor visitor, Type keyType, Type valueType, TContext context, Stack path) + { + visitor.VisitMappingStart(dictionary, keyType, valueType, context); + + var isDynamic = dictionary.Type.FullName!.Equals("System.Dynamic.ExpandoObject"); + foreach (DictionaryEntry? entry in (IDictionary)dictionary.NonNullValue()) + { + var entryValue = entry!.Value; + var keyValue = isDynamic ? namingConvention.Apply(entryValue.Key.ToString()!) : entryValue.Key; + var key = GetObjectDescriptor(keyValue, keyType); + var value = GetObjectDescriptor(entryValue.Value, valueType); + + if (visitor.EnterMapping(key, value, context)) + { + Traverse(keyValue, key, visitor, context, path); + Traverse(keyValue, value, visitor, context, path); + } + } + + visitor.VisitMappingEnd(dictionary, context); + } + + private void TraverseList(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) + { + var enumerableType = ReflectionUtility.GetImplementedGenericInterface(value.Type, typeof(IEnumerable<>)); + var itemType = enumerableType != null ? enumerableType.GetGenericArguments()[0] : typeof(object); + + visitor.VisitSequenceStart(value, itemType, context); + + var index = 0; + + foreach (var item in (IEnumerable)value.NonNullValue()) + { + Traverse(index, GetObjectDescriptor(item, itemType), visitor, context, path); + ++index; + } + + visitor.VisitSequenceEnd(value, context); + } + + protected virtual void TraverseProperties(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) + { + visitor.VisitMappingStart(value, typeof(string), typeof(object), context); + + var source = value.NonNullValue(); + foreach (var propertyDescriptor in typeDescriptor.GetProperties(value.Type, source)) + { + var propertyValue = propertyDescriptor.Read(source); + if (visitor.EnterMapping(propertyDescriptor, propertyValue, context)) + { + Traverse(propertyDescriptor.Name, new ObjectDescriptor(propertyDescriptor.Name, typeof(string), typeof(string), ScalarStyle.Plain), visitor, context, path); + Traverse(propertyDescriptor.Name, propertyValue, visitor, context, path); + } + } + + visitor.VisitMappingEnd(value, context); + } + + private IObjectDescriptor GetObjectDescriptor(object? value, Type staticType) + { + return new ObjectDescriptor(value, typeResolver.Resolve(staticType, value), staticType); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphTraversalStrategies/RoundtripObjectGraphTraversalStrategy.cs b/YamlDotNet/Serialization/ObjectGraphTraversalStrategies/RoundtripObjectGraphTraversalStrategy.cs index dec0b0dc5..0f774e999 100644 --- a/YamlDotNet/Serialization/ObjectGraphTraversalStrategies/RoundtripObjectGraphTraversalStrategy.cs +++ b/YamlDotNet/Serialization/ObjectGraphTraversalStrategies/RoundtripObjectGraphTraversalStrategy.cs @@ -1,53 +1,53 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace YamlDotNet.Serialization.ObjectGraphTraversalStrategies -{ - /// - /// An implementation of that traverses - /// properties that are read/write, collections and dictionaries, while ensuring that - /// the graph can be regenerated from the resulting document. - /// - public class RoundtripObjectGraphTraversalStrategy : FullObjectGraphTraversalStrategy - { - private readonly IEnumerable converters; - - public RoundtripObjectGraphTraversalStrategy(IEnumerable converters, ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention namingConvention) - : base(typeDescriptor, typeResolver, maxRecursion, namingConvention) - { - this.converters = converters; - } - - protected override void TraverseProperties(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) - { - if (!value.Type.HasDefaultConstructor() && !converters.Any(c => c.Accepts(value.Type))) - { - throw new InvalidOperationException($"Type '{value.Type}' cannot be deserialized because it does not have a default constructor or a type converter."); - } - - base.TraverseProperties(value, visitor, context, path); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace YamlDotNet.Serialization.ObjectGraphTraversalStrategies +{ + /// + /// An implementation of that traverses + /// properties that are read/write, collections and dictionaries, while ensuring that + /// the graph can be regenerated from the resulting document. + /// + public class RoundtripObjectGraphTraversalStrategy : FullObjectGraphTraversalStrategy + { + private readonly IEnumerable converters; + + public RoundtripObjectGraphTraversalStrategy(IEnumerable converters, ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention namingConvention) + : base(typeDescriptor, typeResolver, maxRecursion, namingConvention) + { + this.converters = converters; + } + + protected override void TraverseProperties(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path) + { + if (!value.Type.HasDefaultConstructor() && !converters.Any(c => c.Accepts(value.Type))) + { + throw new InvalidOperationException($"Type '{value.Type}' cannot be deserialized because it does not have a default constructor or a type converter."); + } + + base.TraverseProperties(value, visitor, context, path); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphTraversalStrategyFactory.cs b/YamlDotNet/Serialization/ObjectGraphTraversalStrategyFactory.cs index 03683e895..29569f0f3 100644 --- a/YamlDotNet/Serialization/ObjectGraphTraversalStrategyFactory.cs +++ b/YamlDotNet/Serialization/ObjectGraphTraversalStrategyFactory.cs @@ -1,41 +1,41 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; - -namespace YamlDotNet.Serialization -{ - /// - /// A factory method for creating instances - /// - /// The type inspector to be used by the traversal strategy. - /// The type resolver to be used by the traversal strategy. - /// The type converters to be used by the traversal strategy. - /// The maximum object depth to be supported by the traversal strategy. - /// - public delegate IObjectGraphTraversalStrategy - ObjectGraphTraversalStrategyFactory( - ITypeInspector typeInspector, - ITypeResolver typeResolver, - IEnumerable typeConverters, - int maximumRecursion - ); -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; + +namespace YamlDotNet.Serialization +{ + /// + /// A factory method for creating instances + /// + /// The type inspector to be used by the traversal strategy. + /// The type resolver to be used by the traversal strategy. + /// The type converters to be used by the traversal strategy. + /// The maximum object depth to be supported by the traversal strategy. + /// + public delegate IObjectGraphTraversalStrategy + ObjectGraphTraversalStrategyFactory( + ITypeInspector typeInspector, + ITypeResolver typeResolver, + IEnumerable typeConverters, + int maximumRecursion + ); +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigner.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigner.cs index 268f3157c..5c9105091 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigner.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigner.cs @@ -1,105 +1,105 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Globalization; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.ObjectGraphVisitors -{ - public sealed class AnchorAssigner : PreProcessingPhaseObjectGraphVisitorSkeleton, IAliasProvider - { - private class AnchorAssignment - { - public AnchorName Anchor; - } - - private readonly IDictionary assignments = new Dictionary(); - private uint nextId; - - public AnchorAssigner(IEnumerable typeConverters) - : base(typeConverters) - { - } - - protected override bool Enter(IObjectDescriptor value) - { - if (value.Value != null && assignments.TryGetValue(value.Value, out var assignment)) - { - if (assignment.Anchor.IsEmpty) - { - assignment.Anchor = new AnchorName("o" + nextId.ToString(CultureInfo.InvariantCulture)); - ++nextId; - } - return false; - } - - return true; - } - - protected override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value) - { - return true; - } - - protected override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value) - { - return true; - } - - protected override void VisitScalar(IObjectDescriptor scalar) - { - // Do not assign anchors to scalars - } - - protected override void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType) - { - VisitObject(mapping); - } - - protected override void VisitMappingEnd(IObjectDescriptor mapping) { } - - protected override void VisitSequenceStart(IObjectDescriptor sequence, Type elementType) - { - VisitObject(sequence); - } - - protected override void VisitSequenceEnd(IObjectDescriptor sequence) { } - - private void VisitObject(IObjectDescriptor value) - { - if (value.Value != null) - { - assignments.Add(value.Value, new AnchorAssignment()); - } - } - - AnchorName IAliasProvider.GetAlias(object target) - { - if (target != null && assignments.TryGetValue(target, out var assignment)) - { - return assignment.Anchor; - } - return AnchorName.Empty; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Globalization; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.ObjectGraphVisitors +{ + public sealed class AnchorAssigner : PreProcessingPhaseObjectGraphVisitorSkeleton, IAliasProvider + { + private class AnchorAssignment + { + public AnchorName Anchor; + } + + private readonly IDictionary assignments = new Dictionary(); + private uint nextId; + + public AnchorAssigner(IEnumerable typeConverters) + : base(typeConverters) + { + } + + protected override bool Enter(IObjectDescriptor value) + { + if (value.Value != null && assignments.TryGetValue(value.Value, out var assignment)) + { + if (assignment.Anchor.IsEmpty) + { + assignment.Anchor = new AnchorName("o" + nextId.ToString(CultureInfo.InvariantCulture)); + ++nextId; + } + return false; + } + + return true; + } + + protected override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value) + { + return true; + } + + protected override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value) + { + return true; + } + + protected override void VisitScalar(IObjectDescriptor scalar) + { + // Do not assign anchors to scalars + } + + protected override void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType) + { + VisitObject(mapping); + } + + protected override void VisitMappingEnd(IObjectDescriptor mapping) { } + + protected override void VisitSequenceStart(IObjectDescriptor sequence, Type elementType) + { + VisitObject(sequence); + } + + protected override void VisitSequenceEnd(IObjectDescriptor sequence) { } + + private void VisitObject(IObjectDescriptor value) + { + if (value.Value != null) + { + assignments.Add(value.Value, new AnchorAssignment()); + } + } + + AnchorName IAliasProvider.GetAlias(object target) + { + if (target != null && assignments.TryGetValue(target, out var assignment)) + { + return assignment.Anchor; + } + return AnchorName.Empty; + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigningObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigningObjectGraphVisitor.cs index 45a32818b..7882ac8b8 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigningObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigningObjectGraphVisitor.cs @@ -1,78 +1,78 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.ObjectGraphVisitors -{ - public sealed class AnchorAssigningObjectGraphVisitor : ChainedObjectGraphVisitor - { - private readonly IEventEmitter eventEmitter; - private readonly IAliasProvider aliasProvider; - private readonly HashSet emittedAliases = new HashSet(); - - public AnchorAssigningObjectGraphVisitor(IObjectGraphVisitor nextVisitor, IEventEmitter eventEmitter, IAliasProvider aliasProvider) - : base(nextVisitor) - { - this.eventEmitter = eventEmitter; - this.aliasProvider = aliasProvider; - } - - public override bool Enter(IObjectDescriptor value, IEmitter context) - { - if (value.Value != null) - { - var alias = aliasProvider.GetAlias(value.Value); - if (!alias.IsEmpty && !emittedAliases.Add(alias)) - { - var aliasEventInfo = new AliasEventInfo(value, alias); - eventEmitter.Emit(aliasEventInfo, context); - return aliasEventInfo.NeedsExpansion; - } - } - return base.Enter(value, context); - } - - public override void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context) - { - var anchor = aliasProvider.GetAlias(mapping.NonNullValue()); - eventEmitter.Emit(new MappingStartEventInfo(mapping) { Anchor = anchor }, context); - } - - public override void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context) - { - var anchor = aliasProvider.GetAlias(sequence.NonNullValue()); - eventEmitter.Emit(new SequenceStartEventInfo(sequence) { Anchor = anchor }, context); - } - - public override void VisitScalar(IObjectDescriptor scalar, IEmitter context) - { - var scalarInfo = new ScalarEventInfo(scalar); - if (scalar.Value != null) - { - scalarInfo.Anchor = aliasProvider.GetAlias(scalar.Value); - } - eventEmitter.Emit(scalarInfo, context); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.ObjectGraphVisitors +{ + public sealed class AnchorAssigningObjectGraphVisitor : ChainedObjectGraphVisitor + { + private readonly IEventEmitter eventEmitter; + private readonly IAliasProvider aliasProvider; + private readonly HashSet emittedAliases = new HashSet(); + + public AnchorAssigningObjectGraphVisitor(IObjectGraphVisitor nextVisitor, IEventEmitter eventEmitter, IAliasProvider aliasProvider) + : base(nextVisitor) + { + this.eventEmitter = eventEmitter; + this.aliasProvider = aliasProvider; + } + + public override bool Enter(IObjectDescriptor value, IEmitter context) + { + if (value.Value != null) + { + var alias = aliasProvider.GetAlias(value.Value); + if (!alias.IsEmpty && !emittedAliases.Add(alias)) + { + var aliasEventInfo = new AliasEventInfo(value, alias); + eventEmitter.Emit(aliasEventInfo, context); + return aliasEventInfo.NeedsExpansion; + } + } + return base.Enter(value, context); + } + + public override void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context) + { + var anchor = aliasProvider.GetAlias(mapping.NonNullValue()); + eventEmitter.Emit(new MappingStartEventInfo(mapping) { Anchor = anchor }, context); + } + + public override void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context) + { + var anchor = aliasProvider.GetAlias(sequence.NonNullValue()); + eventEmitter.Emit(new SequenceStartEventInfo(sequence) { Anchor = anchor }, context); + } + + public override void VisitScalar(IObjectDescriptor scalar, IEmitter context) + { + var scalarInfo = new ScalarEventInfo(scalar); + if (scalar.Value != null) + { + scalarInfo.Anchor = aliasProvider.GetAlias(scalar.Value); + } + eventEmitter.Emit(scalarInfo, context); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/ChainedObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/ChainedObjectGraphVisitor.cs index c8f689889..231a78077 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/ChainedObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/ChainedObjectGraphVisitor.cs @@ -1,76 +1,76 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.ObjectGraphVisitors -{ - public abstract class ChainedObjectGraphVisitor : IObjectGraphVisitor - { - private readonly IObjectGraphVisitor nextVisitor; - - protected ChainedObjectGraphVisitor(IObjectGraphVisitor nextVisitor) - { - this.nextVisitor = nextVisitor; - } - - public virtual bool Enter(IObjectDescriptor value, IEmitter context) - { - return nextVisitor.Enter(value, context); - } - - public virtual bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context) - { - return nextVisitor.EnterMapping(key, value, context); - } - - public virtual bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) - { - return nextVisitor.EnterMapping(key, value, context); - } - - public virtual void VisitScalar(IObjectDescriptor scalar, IEmitter context) - { - nextVisitor.VisitScalar(scalar, context); - } - - public virtual void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context) - { - nextVisitor.VisitMappingStart(mapping, keyType, valueType, context); - } - - public virtual void VisitMappingEnd(IObjectDescriptor mapping, IEmitter context) - { - nextVisitor.VisitMappingEnd(mapping, context); - } - - public virtual void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context) - { - nextVisitor.VisitSequenceStart(sequence, elementType, context); - } - - public virtual void VisitSequenceEnd(IObjectDescriptor sequence, IEmitter context) - { - nextVisitor.VisitSequenceEnd(sequence, context); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.ObjectGraphVisitors +{ + public abstract class ChainedObjectGraphVisitor : IObjectGraphVisitor + { + private readonly IObjectGraphVisitor nextVisitor; + + protected ChainedObjectGraphVisitor(IObjectGraphVisitor nextVisitor) + { + this.nextVisitor = nextVisitor; + } + + public virtual bool Enter(IObjectDescriptor value, IEmitter context) + { + return nextVisitor.Enter(value, context); + } + + public virtual bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context) + { + return nextVisitor.EnterMapping(key, value, context); + } + + public virtual bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) + { + return nextVisitor.EnterMapping(key, value, context); + } + + public virtual void VisitScalar(IObjectDescriptor scalar, IEmitter context) + { + nextVisitor.VisitScalar(scalar, context); + } + + public virtual void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context) + { + nextVisitor.VisitMappingStart(mapping, keyType, valueType, context); + } + + public virtual void VisitMappingEnd(IObjectDescriptor mapping, IEmitter context) + { + nextVisitor.VisitMappingEnd(mapping, context); + } + + public virtual void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context) + { + nextVisitor.VisitSequenceStart(sequence, elementType, context); + } + + public virtual void VisitSequenceEnd(IObjectDescriptor sequence, IEmitter context) + { + nextVisitor.VisitSequenceEnd(sequence, context); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/CommentsObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/CommentsObjectGraphVisitor.cs index 2d53d5afa..c5e9f57f3 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/CommentsObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/CommentsObjectGraphVisitor.cs @@ -1,44 +1,44 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.ObjectGraphVisitors -{ - public sealed class CommentsObjectGraphVisitor : ChainedObjectGraphVisitor - { - public CommentsObjectGraphVisitor(IObjectGraphVisitor nextVisitor) - : base(nextVisitor) - { - } - - public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) - { - var yamlMember = key.GetCustomAttribute(); - if (yamlMember?.Description != null) - { - context.Emit(new Core.Events.Comment(yamlMember.Description, false)); - } - - return base.EnterMapping(key, value, context); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.ObjectGraphVisitors +{ + public sealed class CommentsObjectGraphVisitor : ChainedObjectGraphVisitor + { + public CommentsObjectGraphVisitor(IObjectGraphVisitor nextVisitor) + : base(nextVisitor) + { + } + + public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) + { + var yamlMember = key.GetCustomAttribute(); + if (yamlMember?.Description != null) + { + context.Emit(new Core.Events.Comment(yamlMember.Description, false)); + } + + return base.EnterMapping(key, value, context); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/CustomSerializationObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/CustomSerializationObjectGraphVisitor.cs index a0f8b6319..56e3bf6ad 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/CustomSerializationObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/CustomSerializationObjectGraphVisitor.cs @@ -1,69 +1,69 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using System.Linq; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.ObjectGraphVisitors -{ - public sealed class CustomSerializationObjectGraphVisitor : ChainedObjectGraphVisitor - { - private readonly IEnumerable typeConverters; - private readonly ObjectSerializer nestedObjectSerializer; - - public CustomSerializationObjectGraphVisitor(IObjectGraphVisitor nextVisitor, IEnumerable typeConverters, ObjectSerializer nestedObjectSerializer) - : base(nextVisitor) - { - this.typeConverters = typeConverters != null - ? typeConverters.ToList() - : Enumerable.Empty(); - - this.nestedObjectSerializer = nestedObjectSerializer; - } - - public override bool Enter(IObjectDescriptor value, IEmitter context) - { - var typeConverter = typeConverters.FirstOrDefault(t => t.Accepts(value.Type)); - if (typeConverter != null) - { - typeConverter.WriteYaml(context, value.Value, value.Type); - return false; - } - - if (value.Value is IYamlConvertible convertible) - { - convertible.Write(context, nestedObjectSerializer); - return false; - } - -#pragma warning disable 0618 // IYamlSerializable is obsolete - if (value.Value is IYamlSerializable serializable) - { - serializable.WriteYaml(context); - return false; - } -#pragma warning restore - - return base.Enter(value, context); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using System.Linq; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.ObjectGraphVisitors +{ + public sealed class CustomSerializationObjectGraphVisitor : ChainedObjectGraphVisitor + { + private readonly IEnumerable typeConverters; + private readonly ObjectSerializer nestedObjectSerializer; + + public CustomSerializationObjectGraphVisitor(IObjectGraphVisitor nextVisitor, IEnumerable typeConverters, ObjectSerializer nestedObjectSerializer) + : base(nextVisitor) + { + this.typeConverters = typeConverters != null + ? typeConverters.ToList() + : Enumerable.Empty(); + + this.nestedObjectSerializer = nestedObjectSerializer; + } + + public override bool Enter(IObjectDescriptor value, IEmitter context) + { + var typeConverter = typeConverters.FirstOrDefault(t => t.Accepts(value.Type)); + if (typeConverter != null) + { + typeConverter.WriteYaml(context, value.Value, value.Type); + return false; + } + + if (value.Value is IYamlConvertible convertible) + { + convertible.Write(context, nestedObjectSerializer); + return false; + } + +#pragma warning disable 0618 // IYamlSerializable is obsolete + if (value.Value is IYamlSerializable serializable) + { + serializable.WriteYaml(context); + return false; + } +#pragma warning restore + + return base.Enter(value, context); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultExclusiveObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultExclusiveObjectGraphVisitor.cs index f069ca047..91acd03f0 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultExclusiveObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultExclusiveObjectGraphVisitor.cs @@ -1,58 +1,58 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.ComponentModel; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.ObjectGraphVisitors -{ - - public sealed class DefaultExclusiveObjectGraphVisitor : ChainedObjectGraphVisitor - { - public DefaultExclusiveObjectGraphVisitor(IObjectGraphVisitor nextVisitor) - : base(nextVisitor) - { - } - - private static object? GetDefault(Type type) - { - return type.IsValueType() ? Activator.CreateInstance(type) : null; - } - - public override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context) - { - return !Equals(value.Value, GetDefault(value.Type)) - && base.EnterMapping(key, value, context); - } - - public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) - { - var defaultValueAttribute = key.GetCustomAttribute(); - var defaultValue = defaultValueAttribute != null - ? defaultValueAttribute.Value - : GetDefault(key.Type); - - return !Equals(value.Value, defaultValue) - && base.EnterMapping(key, value, context); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.ComponentModel; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.ObjectGraphVisitors +{ + + public sealed class DefaultExclusiveObjectGraphVisitor : ChainedObjectGraphVisitor + { + public DefaultExclusiveObjectGraphVisitor(IObjectGraphVisitor nextVisitor) + : base(nextVisitor) + { + } + + private static object? GetDefault(Type type) + { + return type.IsValueType() ? Activator.CreateInstance(type) : null; + } + + public override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context) + { + return !Equals(value.Value, GetDefault(value.Type)) + && base.EnterMapping(key, value, context); + } + + public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) + { + var defaultValueAttribute = key.GetCustomAttribute(); + var defaultValue = defaultValueAttribute != null + ? defaultValueAttribute.Value + : GetDefault(key.Type); + + return !Equals(value.Value, defaultValue) + && base.EnterMapping(key, value, context); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs index 7e478fa2a..982a23e63 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs @@ -1,91 +1,91 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections; -using System.ComponentModel; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.ObjectGraphVisitors -{ - public sealed class DefaultValuesObjectGraphVisitor : ChainedObjectGraphVisitor - { - private readonly DefaultValuesHandling handling; - - public DefaultValuesObjectGraphVisitor(DefaultValuesHandling handling, IObjectGraphVisitor nextVisitor) - : base(nextVisitor) - { - this.handling = handling; - } - - private static object? GetDefault(Type type) - { - return type.IsValueType() ? Activator.CreateInstance(type) : null; - } - - public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) - { - var configuration = handling; - var yamlMember = key.GetCustomAttribute(); - if (yamlMember != null && yamlMember.IsDefaultValuesHandlingSpecified) - { - configuration = yamlMember.DefaultValuesHandling; - } - - if ((configuration & DefaultValuesHandling.OmitNull) != 0) - { - if (value.Value is null) - { - return false; - } - } - - if ((configuration & DefaultValuesHandling.OmitEmptyCollections) != 0) - { - if (value.Value is IEnumerable enumerable) - { - var enumerator = enumerable.GetEnumerator(); - var canMoveNext = enumerator.MoveNext(); - if (enumerator is IDisposable disposable) - { - disposable.Dispose(); - } - - if (!canMoveNext) - { - return false; - } - } - } - - if ((configuration & DefaultValuesHandling.OmitDefaults) != 0) - { - var defaultValue = key.GetCustomAttribute()?.Value ?? GetDefault(key.Type); - if (Equals(value.Value, defaultValue)) - { - return false; - } - } - - return base.EnterMapping(key, value, context); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections; +using System.ComponentModel; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.ObjectGraphVisitors +{ + public sealed class DefaultValuesObjectGraphVisitor : ChainedObjectGraphVisitor + { + private readonly DefaultValuesHandling handling; + + public DefaultValuesObjectGraphVisitor(DefaultValuesHandling handling, IObjectGraphVisitor nextVisitor) + : base(nextVisitor) + { + this.handling = handling; + } + + private static object? GetDefault(Type type) + { + return type.IsValueType() ? Activator.CreateInstance(type) : null; + } + + public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) + { + var configuration = handling; + var yamlMember = key.GetCustomAttribute(); + if (yamlMember != null && yamlMember.IsDefaultValuesHandlingSpecified) + { + configuration = yamlMember.DefaultValuesHandling; + } + + if ((configuration & DefaultValuesHandling.OmitNull) != 0) + { + if (value.Value is null) + { + return false; + } + } + + if ((configuration & DefaultValuesHandling.OmitEmptyCollections) != 0) + { + if (value.Value is IEnumerable enumerable) + { + var enumerator = enumerable.GetEnumerator(); + var canMoveNext = enumerator.MoveNext(); + if (enumerator is IDisposable disposable) + { + disposable.Dispose(); + } + + if (!canMoveNext) + { + return false; + } + } + } + + if ((configuration & DefaultValuesHandling.OmitDefaults) != 0) + { + var defaultValue = key.GetCustomAttribute()?.Value ?? GetDefault(key.Type); + if (Equals(value.Value, defaultValue)) + { + return false; + } + } + + return base.EnterMapping(key, value, context); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/EmittingObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/EmittingObjectGraphVisitor.cs index 27050a9f5..76dfa22d3 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/EmittingObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/EmittingObjectGraphVisitor.cs @@ -1,76 +1,76 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.ObjectGraphVisitors -{ - public sealed class EmittingObjectGraphVisitor : IObjectGraphVisitor - { - private readonly IEventEmitter eventEmitter; - - public EmittingObjectGraphVisitor(IEventEmitter eventEmitter) - { - this.eventEmitter = eventEmitter; - } - - bool IObjectGraphVisitor.Enter(IObjectDescriptor value, IEmitter context) - { - return true; - } - - bool IObjectGraphVisitor.EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context) - { - return true; - } - - bool IObjectGraphVisitor.EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) - { - return true; - } - - void IObjectGraphVisitor.VisitScalar(IObjectDescriptor scalar, IEmitter context) - { - eventEmitter.Emit(new ScalarEventInfo(scalar), context); - } - - void IObjectGraphVisitor.VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context) - { - eventEmitter.Emit(new MappingStartEventInfo(mapping), context); - } - - void IObjectGraphVisitor.VisitMappingEnd(IObjectDescriptor mapping, IEmitter context) - { - eventEmitter.Emit(new MappingEndEventInfo(mapping), context); - } - - void IObjectGraphVisitor.VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context) - { - eventEmitter.Emit(new SequenceStartEventInfo(sequence), context); - } - - void IObjectGraphVisitor.VisitSequenceEnd(IObjectDescriptor sequence, IEmitter context) - { - eventEmitter.Emit(new SequenceEndEventInfo(sequence), context); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.ObjectGraphVisitors +{ + public sealed class EmittingObjectGraphVisitor : IObjectGraphVisitor + { + private readonly IEventEmitter eventEmitter; + + public EmittingObjectGraphVisitor(IEventEmitter eventEmitter) + { + this.eventEmitter = eventEmitter; + } + + bool IObjectGraphVisitor.Enter(IObjectDescriptor value, IEmitter context) + { + return true; + } + + bool IObjectGraphVisitor.EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context) + { + return true; + } + + bool IObjectGraphVisitor.EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) + { + return true; + } + + void IObjectGraphVisitor.VisitScalar(IObjectDescriptor scalar, IEmitter context) + { + eventEmitter.Emit(new ScalarEventInfo(scalar), context); + } + + void IObjectGraphVisitor.VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context) + { + eventEmitter.Emit(new MappingStartEventInfo(mapping), context); + } + + void IObjectGraphVisitor.VisitMappingEnd(IObjectDescriptor mapping, IEmitter context) + { + eventEmitter.Emit(new MappingEndEventInfo(mapping), context); + } + + void IObjectGraphVisitor.VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context) + { + eventEmitter.Emit(new SequenceStartEventInfo(sequence), context); + } + + void IObjectGraphVisitor.VisitSequenceEnd(IObjectDescriptor sequence, IEmitter context) + { + eventEmitter.Emit(new SequenceEndEventInfo(sequence), context); + } + } +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/PreProcessingPhaseObjectGraphVisitorSkeleton.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/PreProcessingPhaseObjectGraphVisitorSkeleton.cs index 1cb267f2a..619de4da2 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/PreProcessingPhaseObjectGraphVisitorSkeleton.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/PreProcessingPhaseObjectGraphVisitorSkeleton.cs @@ -1,109 +1,109 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace YamlDotNet.Serialization.ObjectGraphVisitors -{ - /// - /// A base class that simplifies the correct implementation of . - /// - public abstract class PreProcessingPhaseObjectGraphVisitorSkeleton : IObjectGraphVisitor - { - protected readonly IEnumerable typeConverters; - - public PreProcessingPhaseObjectGraphVisitorSkeleton(IEnumerable typeConverters) - { - this.typeConverters = typeConverters != null - ? typeConverters.ToList() - : Enumerable.Empty(); - } - - bool IObjectGraphVisitor.Enter(IObjectDescriptor value, Nothing context) - { - var typeConverter = typeConverters.FirstOrDefault(t => t.Accepts(value.Type)); - if (typeConverter != null) - { - return false; - } - - if (value.Value is IYamlConvertible convertible) - { - return false; - } - -#pragma warning disable 0618 // IYamlSerializable is obsolete - if (value.Value is IYamlSerializable serializable) - { - return false; - } -#pragma warning restore - - return Enter(value); - } - - bool IObjectGraphVisitor.EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, Nothing context) - { - return EnterMapping(key, value); - } - - bool IObjectGraphVisitor.EnterMapping(IObjectDescriptor key, IObjectDescriptor value, Nothing context) - { - return EnterMapping(key, value); - } - - void IObjectGraphVisitor.VisitMappingEnd(IObjectDescriptor mapping, Nothing context) - { - VisitMappingEnd(mapping); - } - - void IObjectGraphVisitor.VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, Nothing context) - { - VisitMappingStart(mapping, keyType, valueType); - } - - void IObjectGraphVisitor.VisitScalar(IObjectDescriptor scalar, Nothing context) - { - VisitScalar(scalar); - } - - void IObjectGraphVisitor.VisitSequenceEnd(IObjectDescriptor sequence, Nothing context) - { - VisitSequenceEnd(sequence); - } - - void IObjectGraphVisitor.VisitSequenceStart(IObjectDescriptor sequence, Type elementType, Nothing context) - { - VisitSequenceStart(sequence, elementType); - } - - protected abstract bool Enter(IObjectDescriptor value); - protected abstract bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value); - protected abstract bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value); - protected abstract void VisitMappingEnd(IObjectDescriptor mapping); - protected abstract void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType); - protected abstract void VisitScalar(IObjectDescriptor scalar); - protected abstract void VisitSequenceEnd(IObjectDescriptor sequence); - protected abstract void VisitSequenceStart(IObjectDescriptor sequence, Type elementType); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace YamlDotNet.Serialization.ObjectGraphVisitors +{ + /// + /// A base class that simplifies the correct implementation of . + /// + public abstract class PreProcessingPhaseObjectGraphVisitorSkeleton : IObjectGraphVisitor + { + protected readonly IEnumerable typeConverters; + + public PreProcessingPhaseObjectGraphVisitorSkeleton(IEnumerable typeConverters) + { + this.typeConverters = typeConverters != null + ? typeConverters.ToList() + : Enumerable.Empty(); + } + + bool IObjectGraphVisitor.Enter(IObjectDescriptor value, Nothing context) + { + var typeConverter = typeConverters.FirstOrDefault(t => t.Accepts(value.Type)); + if (typeConverter != null) + { + return false; + } + + if (value.Value is IYamlConvertible convertible) + { + return false; + } + +#pragma warning disable 0618 // IYamlSerializable is obsolete + if (value.Value is IYamlSerializable serializable) + { + return false; + } +#pragma warning restore + + return Enter(value); + } + + bool IObjectGraphVisitor.EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, Nothing context) + { + return EnterMapping(key, value); + } + + bool IObjectGraphVisitor.EnterMapping(IObjectDescriptor key, IObjectDescriptor value, Nothing context) + { + return EnterMapping(key, value); + } + + void IObjectGraphVisitor.VisitMappingEnd(IObjectDescriptor mapping, Nothing context) + { + VisitMappingEnd(mapping); + } + + void IObjectGraphVisitor.VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, Nothing context) + { + VisitMappingStart(mapping, keyType, valueType); + } + + void IObjectGraphVisitor.VisitScalar(IObjectDescriptor scalar, Nothing context) + { + VisitScalar(scalar); + } + + void IObjectGraphVisitor.VisitSequenceEnd(IObjectDescriptor sequence, Nothing context) + { + VisitSequenceEnd(sequence); + } + + void IObjectGraphVisitor.VisitSequenceStart(IObjectDescriptor sequence, Type elementType, Nothing context) + { + VisitSequenceStart(sequence, elementType); + } + + protected abstract bool Enter(IObjectDescriptor value); + protected abstract bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value); + protected abstract bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value); + protected abstract void VisitMappingEnd(IObjectDescriptor mapping); + protected abstract void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType); + protected abstract void VisitScalar(IObjectDescriptor scalar); + protected abstract void VisitSequenceEnd(IObjectDescriptor sequence); + protected abstract void VisitSequenceStart(IObjectDescriptor sequence, Type elementType); + } +} diff --git a/YamlDotNet/Serialization/PropertyDescriptor.cs b/YamlDotNet/Serialization/PropertyDescriptor.cs index c604ab24a..e92a1221b 100644 --- a/YamlDotNet/Serialization/PropertyDescriptor.cs +++ b/YamlDotNet/Serialization/PropertyDescriptor.cs @@ -1,75 +1,75 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - public sealed class PropertyDescriptor : IPropertyDescriptor - { - private readonly IPropertyDescriptor baseDescriptor; - - public PropertyDescriptor(IPropertyDescriptor baseDescriptor) - { - this.baseDescriptor = baseDescriptor; - Name = baseDescriptor.Name; - } - - public string Name { get; set; } - - public Type Type { get { return baseDescriptor.Type; } } - - public Type? TypeOverride - { - get { return baseDescriptor.TypeOverride; } - set { baseDescriptor.TypeOverride = value; } - } - - public int Order { get; set; } - - public ScalarStyle ScalarStyle - { - get { return baseDescriptor.ScalarStyle; } - set { baseDescriptor.ScalarStyle = value; } - } - - public bool CanWrite - { - get { return baseDescriptor.CanWrite; } - } - - public void Write(object target, object? value) - { - baseDescriptor.Write(target, value); - } - - public T GetCustomAttribute() where T : Attribute - { - return baseDescriptor.GetCustomAttribute(); - } - - public IObjectDescriptor Read(object target) - { - return baseDescriptor.Read(target); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + public sealed class PropertyDescriptor : IPropertyDescriptor + { + private readonly IPropertyDescriptor baseDescriptor; + + public PropertyDescriptor(IPropertyDescriptor baseDescriptor) + { + this.baseDescriptor = baseDescriptor; + Name = baseDescriptor.Name; + } + + public string Name { get; set; } + + public Type Type { get { return baseDescriptor.Type; } } + + public Type? TypeOverride + { + get { return baseDescriptor.TypeOverride; } + set { baseDescriptor.TypeOverride = value; } + } + + public int Order { get; set; } + + public ScalarStyle ScalarStyle + { + get { return baseDescriptor.ScalarStyle; } + set { baseDescriptor.ScalarStyle = value; } + } + + public bool CanWrite + { + get { return baseDescriptor.CanWrite; } + } + + public void Write(object target, object? value) + { + baseDescriptor.Write(target, value); + } + + public T GetCustomAttribute() where T : Attribute + { + return baseDescriptor.GetCustomAttribute(); + } + + public IObjectDescriptor Read(object target) + { + return baseDescriptor.Read(target); + } + } +} diff --git a/YamlDotNet/Serialization/Schemas/FailsafeSchema.cs b/YamlDotNet/Serialization/Schemas/FailsafeSchema.cs index 137d75689..fd73ebc65 100644 --- a/YamlDotNet/Serialization/Schemas/FailsafeSchema.cs +++ b/YamlDotNet/Serialization/Schemas/FailsafeSchema.cs @@ -1,61 +1,61 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.Schemas -{ - public sealed class FailsafeSchema - { - public static class Tags - { - public static readonly TagName Map = new TagName("tag:yaml.org,2002:map"); - public static readonly TagName Seq = new TagName("tag:yaml.org,2002:seq"); - public static readonly TagName Str = new TagName("tag:yaml.org,2002:str"); - } - } - - public sealed class JsonSchema - { - public static class Tags - { - public static readonly TagName Null = new TagName("tag:yaml.org,2002:null"); - public static readonly TagName Bool = new TagName("tag:yaml.org,2002:bool"); - public static readonly TagName Int = new TagName("tag:yaml.org,2002:int"); - public static readonly TagName Float = new TagName("tag:yaml.org,2002:float"); - } - } - - public sealed class CoreSchema - { - public static class Tags - { - } - } - - public sealed class DefaultSchema - { - public static class Tags - { - public static readonly TagName Timestamp = new TagName("tag:yaml.org,2002:timestamp"); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.Schemas +{ + public sealed class FailsafeSchema + { + public static class Tags + { + public static readonly TagName Map = new TagName("tag:yaml.org,2002:map"); + public static readonly TagName Seq = new TagName("tag:yaml.org,2002:seq"); + public static readonly TagName Str = new TagName("tag:yaml.org,2002:str"); + } + } + + public sealed class JsonSchema + { + public static class Tags + { + public static readonly TagName Null = new TagName("tag:yaml.org,2002:null"); + public static readonly TagName Bool = new TagName("tag:yaml.org,2002:bool"); + public static readonly TagName Int = new TagName("tag:yaml.org,2002:int"); + public static readonly TagName Float = new TagName("tag:yaml.org,2002:float"); + } + } + + public sealed class CoreSchema + { + public static class Tags + { + } + } + + public sealed class DefaultSchema + { + public static class Tags + { + public static readonly TagName Timestamp = new TagName("tag:yaml.org,2002:timestamp"); + } + } +} diff --git a/YamlDotNet/Serialization/Serializer.cs b/YamlDotNet/Serialization/Serializer.cs index 9e49be3e6..f0fbf9b88 100644 --- a/YamlDotNet/Serialization/Serializer.cs +++ b/YamlDotNet/Serialization/Serializer.cs @@ -1,144 +1,144 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization -{ - public sealed class Serializer : ISerializer - { - private readonly IValueSerializer valueSerializer; - private readonly EmitterSettings emitterSettings; - - /// - /// Initializes a new instance of using the default configuration. - /// - /// - /// To customize the behavior of the serializer, use . - /// - public Serializer() - : this(new SerializerBuilder().BuildValueSerializer(), EmitterSettings.Default) - { - } - - /// - /// This constructor is private to discourage its use. - /// To invoke it, call the method. - /// - private Serializer(IValueSerializer valueSerializer, EmitterSettings emitterSettings) - { - this.valueSerializer = valueSerializer ?? throw new ArgumentNullException(nameof(valueSerializer)); - this.emitterSettings = emitterSettings ?? throw new ArgumentNullException(nameof(emitterSettings)); - } - - /// - /// Creates a new that uses the specified . - /// This method is available for advanced scenarios. The preferred way to customize the behavior of the - /// deserializer is to use . - /// - public static Serializer FromValueSerializer(IValueSerializer valueSerializer, EmitterSettings emitterSettings) - { - return new Serializer(valueSerializer, emitterSettings); - } - - /// - /// Serializes the specified object. - /// - /// The where to serialize the object. - /// The object to serialize. - public void Serialize(TextWriter writer, object graph) - { - Serialize(new Emitter(writer, emitterSettings), graph); - } - - /// - /// Serializes the specified object into a string. - /// - /// The object to serialize. - public string Serialize(object graph) - { - using var buffer = new StringWriter(); - Serialize(buffer, graph); - return buffer.ToString(); - } - - /// - /// Serializes the specified object. - /// - /// The where to serialize the object. - /// The object to serialize. - /// The static type of the object to serialize. - public void Serialize(TextWriter writer, object graph, Type type) - { - Serialize(new Emitter(writer, emitterSettings), graph, type); - } - - /// - /// Serializes the specified object. - /// - /// The where to serialize the object. - /// The object to serialize. - public void Serialize(IEmitter emitter, object graph) - { - if (emitter == null) - { - throw new ArgumentNullException(nameof(emitter)); - } - - EmitDocument(emitter, graph, null); - } - - /// - /// Serializes the specified object. - /// - /// The where to serialize the object. - /// The object to serialize. - /// The static type of the object to serialize. - public void Serialize(IEmitter emitter, object graph, Type type) - { - if (emitter == null) - { - throw new ArgumentNullException(nameof(emitter)); - } - - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - EmitDocument(emitter, graph, type); - } - - private void EmitDocument(IEmitter emitter, object graph, Type? type) - { - emitter.Emit(new StreamStart()); - emitter.Emit(new DocumentStart()); - - valueSerializer.SerializeValue(emitter, graph, type); - - emitter.Emit(new DocumentEnd(true)); - emitter.Emit(new StreamEnd()); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.IO; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization +{ + public sealed class Serializer : ISerializer + { + private readonly IValueSerializer valueSerializer; + private readonly EmitterSettings emitterSettings; + + /// + /// Initializes a new instance of using the default configuration. + /// + /// + /// To customize the behavior of the serializer, use . + /// + public Serializer() + : this(new SerializerBuilder().BuildValueSerializer(), EmitterSettings.Default) + { + } + + /// + /// This constructor is private to discourage its use. + /// To invoke it, call the method. + /// + private Serializer(IValueSerializer valueSerializer, EmitterSettings emitterSettings) + { + this.valueSerializer = valueSerializer ?? throw new ArgumentNullException(nameof(valueSerializer)); + this.emitterSettings = emitterSettings ?? throw new ArgumentNullException(nameof(emitterSettings)); + } + + /// + /// Creates a new that uses the specified . + /// This method is available for advanced scenarios. The preferred way to customize the behavior of the + /// deserializer is to use . + /// + public static Serializer FromValueSerializer(IValueSerializer valueSerializer, EmitterSettings emitterSettings) + { + return new Serializer(valueSerializer, emitterSettings); + } + + /// + /// Serializes the specified object. + /// + /// The where to serialize the object. + /// The object to serialize. + public void Serialize(TextWriter writer, object graph) + { + Serialize(new Emitter(writer, emitterSettings), graph); + } + + /// + /// Serializes the specified object into a string. + /// + /// The object to serialize. + public string Serialize(object graph) + { + using var buffer = new StringWriter(); + Serialize(buffer, graph); + return buffer.ToString(); + } + + /// + /// Serializes the specified object. + /// + /// The where to serialize the object. + /// The object to serialize. + /// The static type of the object to serialize. + public void Serialize(TextWriter writer, object graph, Type type) + { + Serialize(new Emitter(writer, emitterSettings), graph, type); + } + + /// + /// Serializes the specified object. + /// + /// The where to serialize the object. + /// The object to serialize. + public void Serialize(IEmitter emitter, object graph) + { + if (emitter == null) + { + throw new ArgumentNullException(nameof(emitter)); + } + + EmitDocument(emitter, graph, null); + } + + /// + /// Serializes the specified object. + /// + /// The where to serialize the object. + /// The object to serialize. + /// The static type of the object to serialize. + public void Serialize(IEmitter emitter, object graph, Type type) + { + if (emitter == null) + { + throw new ArgumentNullException(nameof(emitter)); + } + + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + EmitDocument(emitter, graph, type); + } + + private void EmitDocument(IEmitter emitter, object graph, Type? type) + { + emitter.Emit(new StreamStart()); + emitter.Emit(new DocumentStart()); + + valueSerializer.SerializeValue(emitter, graph, type); + + emitter.Emit(new DocumentEnd(true)); + emitter.Emit(new StreamEnd()); + } + } +} diff --git a/YamlDotNet/Serialization/SerializerBuilder.cs b/YamlDotNet/Serialization/SerializerBuilder.cs index 2a270e24d..e8134cb68 100755 --- a/YamlDotNet/Serialization/SerializerBuilder.cs +++ b/YamlDotNet/Serialization/SerializerBuilder.cs @@ -1,603 +1,603 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Serialization.Converters; -using YamlDotNet.Serialization.EventEmitters; -using YamlDotNet.Serialization.NamingConventions; -using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; -using YamlDotNet.Serialization.ObjectGraphVisitors; -using YamlDotNet.Serialization.TypeInspectors; -using YamlDotNet.Serialization.TypeResolvers; - -namespace YamlDotNet.Serialization -{ - - /// - /// Creates and configures instances of . - /// This class is used to customize the behavior of . Use the relevant methods - /// to apply customizations, then call to create an instance of the serializer - /// with the desired customizations. - /// - public sealed class SerializerBuilder : BuilderSkeleton - { - private ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory; - private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; - private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; - private readonly LazyComponentRegistrationList eventEmitterFactories; - private readonly IDictionary tagMappings = new Dictionary(); - private int maximumRecursion = 50; - private EmitterSettings emitterSettings = EmitterSettings.Default; - private DefaultValuesHandling defaultValuesHandlingConfiguration = DefaultValuesHandling.Preserve; - private bool quoteNecessaryStrings; - - public SerializerBuilder() - : base(new DynamicTypeResolver()) - { - typeInspectorFactories.Add(typeof(CachedTypeInspector), inner => new CachedTypeInspector(inner)); - typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), inner => namingConvention is NullNamingConvention ? inner : new NamingConventionTypeInspector(inner, namingConvention)); - typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), inner => new YamlAttributesTypeInspector(inner)); - typeInspectorFactories.Add(typeof(YamlAttributeOverridesInspector), inner => overrides != null ? new YamlAttributeOverridesInspector(inner, overrides.Clone()) : inner); - - preProcessingPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList, IObjectGraphVisitor> - { - { typeof(AnchorAssigner), typeConverters => new AnchorAssigner(typeConverters) } - }; - - emissionPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList> - { - { - typeof(CustomSerializationObjectGraphVisitor), - args => new CustomSerializationObjectGraphVisitor(args.InnerVisitor, args.TypeConverters, args.NestedObjectSerializer) - }, - { - typeof(AnchorAssigningObjectGraphVisitor), - args => new AnchorAssigningObjectGraphVisitor(args.InnerVisitor, args.EventEmitter, args.GetPreProcessingPhaseObjectGraphVisitor()) - }, - { - typeof(DefaultValuesObjectGraphVisitor), - args => new DefaultValuesObjectGraphVisitor(defaultValuesHandlingConfiguration, args.InnerVisitor) - }, - { - typeof(CommentsObjectGraphVisitor), - args => new CommentsObjectGraphVisitor(args.InnerVisitor) - } - }; - - eventEmitterFactories = new LazyComponentRegistrationList - { - { typeof(TypeAssigningEventEmitter), inner => new TypeAssigningEventEmitter(inner, false, tagMappings, quoteNecessaryStrings) } - }; - - objectGraphTraversalStrategyFactory = (typeInspector, typeResolver, typeConverters, maximumRecursion) => new FullObjectGraphTraversalStrategy(typeInspector, typeResolver, maximumRecursion, namingConvention); - } - - protected override SerializerBuilder Self { get { return this; } } - - /// - /// Put double quotes around strings that need it, for example Null, True, False, a number. This should be called before any other "With" methods if you want this feature enabled. - /// - public SerializerBuilder WithQuotingNecessaryStrings() - { - quoteNecessaryStrings = true; - return this; - } - - /// - /// Sets the maximum recursion that is allowed while traversing the object graph. The default value is 50. - /// - public SerializerBuilder WithMaximumRecursion(int maximumRecursion) - { - if (maximumRecursion <= 0) - { - throw new ArgumentOutOfRangeException(nameof(maximumRecursion), $"The maximum recursion specified ({maximumRecursion}) is invalid. It should be a positive integer."); - } - - this.maximumRecursion = maximumRecursion; - return this; - } - - /// - /// Registers an additional to be used by the serializer. - /// - /// A function that instantiates the event emitter. - public SerializerBuilder WithEventEmitter(Func eventEmitterFactory) - where TEventEmitter : IEventEmitter - { - return WithEventEmitter(eventEmitterFactory, w => w.OnTop()); - } - - /// - /// Registers an additional to be used by the serializer. - /// - /// A function that instantiates the event emitter. - /// Configures the location where to insert the - public SerializerBuilder WithEventEmitter( - Func eventEmitterFactory, - Action> where - ) - where TEventEmitter : IEventEmitter - { - if (eventEmitterFactory == null) - { - throw new ArgumentNullException(nameof(eventEmitterFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(eventEmitterFactories.CreateRegistrationLocationSelector(typeof(TEventEmitter), inner => eventEmitterFactory(inner))); - return Self; - } - - /// - /// Registers an additional to be used by the serializer. - /// - /// A function that instantiates the event emitter based on a previously registered . - /// Configures the location where to insert the - public SerializerBuilder WithEventEmitter( - WrapperFactory eventEmitterFactory, - Action> where - ) - where TEventEmitter : IEventEmitter - { - if (eventEmitterFactory == null) - { - throw new ArgumentNullException(nameof(eventEmitterFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(eventEmitterFactories.CreateTrackingRegistrationLocationSelector(typeof(TEventEmitter), (wrapped, inner) => eventEmitterFactory(wrapped, inner))); - return Self; - } - - /// - /// Unregisters an existing of type . - /// - public SerializerBuilder WithoutEventEmitter() - where TEventEmitter : IEventEmitter - { - return WithoutEventEmitter(typeof(TEventEmitter)); - } - - /// - /// Unregisters an existing of type . - /// - public SerializerBuilder WithoutEventEmitter(Type eventEmitterType) - { - if (eventEmitterType == null) - { - throw new ArgumentNullException(nameof(eventEmitterType)); - } - - eventEmitterFactories.Remove(eventEmitterType); - return this; - } - - /// - /// Registers a tag mapping. - /// - public override SerializerBuilder WithTagMapping(TagName tag, Type type) - { - if (tag.IsEmpty) - { - throw new ArgumentException("Non-specific tags cannot be maped"); - } - - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - if (tagMappings.TryGetValue(type, out var alreadyRegisteredTag)) - { - throw new ArgumentException($"Type already has a registered tag '{alreadyRegisteredTag}' for type '{type.FullName}'", nameof(type)); - } - - tagMappings.Add(type, tag); - return this; - } - - /// - /// Unregisters an existing tag mapping. - /// - public SerializerBuilder WithoutTagMapping(Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - if (!tagMappings.Remove(type)) - { - throw new KeyNotFoundException($"Tag for type '{type.FullName}' is not registered"); - } - return this; - } - - /// - /// Ensures that it will be possible to deserialize the serialized objects. - /// This option will force the emission of tags and emit only properties with setters. - /// - public SerializerBuilder EnsureRoundtrip() - { - objectGraphTraversalStrategyFactory = (typeInspector, typeResolver, typeConverters, maximumRecursion) => new RoundtripObjectGraphTraversalStrategy( - typeConverters, - typeInspector, - typeResolver, - maximumRecursion, - namingConvention - ); - WithEventEmitter(inner => new TypeAssigningEventEmitter(inner, true, tagMappings, quoteNecessaryStrings), loc => loc.InsteadOf()); - return WithTypeInspector(inner => new ReadableAndWritablePropertiesTypeInspector(inner), loc => loc.OnBottom()); - } - - /// - /// Specifies that, if the same object appears more than once in the - /// serialization graph, it will be serialized each time instead of just once. - /// - /// - /// If the serialization graph contains circular references and this flag is set, - /// a StackOverflowException will be thrown. - /// If this flag is not set, there is a performance penalty because the entire - /// object graph must be walked twice. - /// - public SerializerBuilder DisableAliases() - { - preProcessingPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigner)); - emissionPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigningObjectGraphVisitor)); - return this; - } - - /// - /// Forces every value to be serialized, even if it is the default value for that type. - /// - [Obsolete("The default behavior is now to always emit default values, thefore calling this method has no effect. This behavior is now controlled by ConfigureDefaultValuesHandling.", error: true)] - public SerializerBuilder EmitDefaults() => ConfigureDefaultValuesHandling(DefaultValuesHandling.Preserve); - - /// - /// Configures how properties with default and null values should be handled. The default value is DefaultValuesHandling.Preserve - /// - /// - /// If more control is needed, create a class that extends from ChainedObjectGraphVisitor and override its EnterMapping methods. - /// Then register it as follows: - /// WithEmissionPhaseObjectGraphVisitor(args => new MyDefaultHandlingStrategy(args.InnerVisitor)); - /// - public SerializerBuilder ConfigureDefaultValuesHandling(DefaultValuesHandling configuration) - { - this.defaultValuesHandlingConfiguration = configuration; - return this; - } - - /// - /// Ensures that the result of the serialization is valid JSON. - /// - public SerializerBuilder JsonCompatible() - { - this.emitterSettings = this.emitterSettings - .WithMaxSimpleKeyLength(int.MaxValue) - .WithoutAnchorName(); - - return this - .WithTypeConverter(new GuidConverter(true), w => w.InsteadOf()) - .WithEventEmitter(inner => new JsonEventEmitter(inner), loc => loc.InsteadOf()); - } - - /// - /// Registers an additional to be used by the serializer - /// before emitting an object graph. - /// - /// - /// Registering a visitor in the pre-processing phase enables to traverse the object graph once - /// before actually emitting it. This allows a visitor to collect information about the graph that - /// can be used later by another visitor registered in the emission phase. - /// - /// The type inspector. - public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor) - where TObjectGraphVisitor : IObjectGraphVisitor - { - return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitor, w => w.OnTop()); - } - - /// - /// Registers an additional to be used by the serializer - /// before emitting an object graph. - /// - /// - /// Registering a visitor in the pre-processing phase enables to traverse the object graph once - /// before actually emitting it. This allows a visitor to collect information about the graph that - /// can be used later by another visitor registered in the emission phase. - /// - /// The type inspector. - /// Configures the location where to insert the - public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor( - TObjectGraphVisitor objectGraphVisitor, - Action>> where - ) - where TObjectGraphVisitor : IObjectGraphVisitor - { - if (objectGraphVisitor == null) - { - throw new ArgumentNullException(nameof(objectGraphVisitor)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), _ => objectGraphVisitor)); - return this; - } - - /// - /// Registers an additional to be used by the serializer - /// before emitting an object graph. - /// - /// - /// Registering a visitor in the pre-processing phase enables to traverse the object graph once - /// before actually emitting it. This allows a visitor to collect information about the graph that - /// can be used later by another visitor registered in the emission phase. - /// - /// A factory that creates the based on a previously registered . - /// Configures the location where to insert the - public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor( - WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, - Action>> where - ) - where TObjectGraphVisitor : IObjectGraphVisitor - { - if (objectGraphVisitorFactory == null) - { - throw new ArgumentNullException(nameof(objectGraphVisitorFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (wrapped, _) => objectGraphVisitorFactory(wrapped))); - return this; - } - - /// - /// Unregisters an existing of type . - /// - public SerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor() - where TObjectGraphVisitor : IObjectGraphVisitor - { - return WithoutPreProcessingPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); - } - - /// - /// Unregisters an existing of type . - /// - public SerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor(Type objectGraphVisitorType) - { - if (objectGraphVisitorType == null) - { - throw new ArgumentNullException(nameof(objectGraphVisitorType)); - } - - preProcessingPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); - return this; - } - - /// - /// Registers an to be used by the serializer - /// while traversing the object graph. - /// - /// A function that instantiates the traversal strategy. - public SerializerBuilder WithObjectGraphTraversalStrategyFactory(ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory) - { - this.objectGraphTraversalStrategyFactory = objectGraphTraversalStrategyFactory; - - return this; - } - - /// - /// Registers an additional to be used by the serializer - /// while emitting an object graph. - /// - /// A function that instantiates the type inspector. - public SerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory) - where TObjectGraphVisitor : IObjectGraphVisitor - { - return WithEmissionPhaseObjectGraphVisitor(objectGraphVisitorFactory, w => w.OnTop()); - } - - /// - /// Registers an additional to be used by the serializer - /// while emitting an object graph. - /// - /// A function that instantiates the type inspector. - /// Configures the location where to insert the - public SerializerBuilder WithEmissionPhaseObjectGraphVisitor( - Func objectGraphVisitorFactory, - Action>> where - ) - where TObjectGraphVisitor : IObjectGraphVisitor - { - if (objectGraphVisitorFactory == null) - { - throw new ArgumentNullException(nameof(objectGraphVisitorFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(emissionPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), args => objectGraphVisitorFactory(args))); - return this; - } - - /// - /// Registers an additional to be used by the serializer - /// while emitting an object graph. - /// - /// A function that instantiates the type inspector based on a previously registered . - /// Configures the location where to insert the - public SerializerBuilder WithEmissionPhaseObjectGraphVisitor( - WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, - Action>> where - ) - where TObjectGraphVisitor : IObjectGraphVisitor - { - if (objectGraphVisitorFactory == null) - { - throw new ArgumentNullException(nameof(objectGraphVisitorFactory)); - } - - if (where == null) - { - throw new ArgumentNullException(nameof(where)); - } - - where(emissionPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (wrapped, args) => objectGraphVisitorFactory(wrapped, args))); - return this; - } - - /// - /// Unregisters an existing of type . - /// - public SerializerBuilder WithoutEmissionPhaseObjectGraphVisitor() - where TObjectGraphVisitor : IObjectGraphVisitor - { - return WithoutEmissionPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); - } - - /// - /// Unregisters an existing of type . - /// - public SerializerBuilder WithoutEmissionPhaseObjectGraphVisitor(Type objectGraphVisitorType) - { - if (objectGraphVisitorType == null) - { - throw new ArgumentNullException(nameof(objectGraphVisitorType)); - } - - emissionPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); - return this; - } - - /// - /// Creates sequences with extra indentation - /// - /// - /// list: - /// - item - /// - item - /// - /// - public SerializerBuilder WithIndentedSequences() - { - emitterSettings = emitterSettings.WithIndentedSequences(); - - return this; - } - - /// - /// Creates a new according to the current configuration. - /// - public ISerializer Build() - { - return Serializer.FromValueSerializer(BuildValueSerializer(), emitterSettings); - } - - /// - /// Creates a new that implements the current configuration. - /// This method is available for advanced scenarios. The preferred way to customize the behavior of the - /// deserializer is to use the method. - /// - public IValueSerializer BuildValueSerializer() - { - var typeConverters = BuildTypeConverters(); - var typeInspector = BuildTypeInspector(); - var traversalStrategy = objectGraphTraversalStrategyFactory(typeInspector, typeResolver, typeConverters, maximumRecursion); - var eventEmitter = eventEmitterFactories.BuildComponentChain(new WriterEventEmitter()); - - return new ValueSerializer( - traversalStrategy, - eventEmitter, - typeConverters, - preProcessingPhaseObjectGraphVisitorFactories.Clone(), - emissionPhaseObjectGraphVisitorFactories.Clone() - ); - } - - private class ValueSerializer : IValueSerializer - { - private readonly IObjectGraphTraversalStrategy traversalStrategy; - private readonly IEventEmitter eventEmitter; - private readonly IEnumerable typeConverters; - private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; - private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; - - public ValueSerializer( - IObjectGraphTraversalStrategy traversalStrategy, - IEventEmitter eventEmitter, - IEnumerable typeConverters, - LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories, - LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories - ) - { - this.traversalStrategy = traversalStrategy; - this.eventEmitter = eventEmitter; - this.typeConverters = typeConverters; - this.preProcessingPhaseObjectGraphVisitorFactories = preProcessingPhaseObjectGraphVisitorFactories; - this.emissionPhaseObjectGraphVisitorFactories = emissionPhaseObjectGraphVisitorFactories; - } - - public void SerializeValue(IEmitter emitter, object? value, Type? type) - { - var actualType = type ?? (value != null ? value.GetType() : typeof(object)); - var staticType = type ?? typeof(object); - - var graph = new ObjectDescriptor(value, actualType, staticType); - - var preProcessingPhaseObjectGraphVisitors = preProcessingPhaseObjectGraphVisitorFactories.BuildComponentList(typeConverters); - foreach (var visitor in preProcessingPhaseObjectGraphVisitors) - { - traversalStrategy.Traverse(graph, visitor, default); - } - - void NestedObjectSerializer(object? v, Type? t) => SerializeValue(emitter, v, t); - - var emittingVisitor = emissionPhaseObjectGraphVisitorFactories.BuildComponentChain( - new EmittingObjectGraphVisitor(eventEmitter), - inner => new EmissionPhaseObjectGraphVisitorArgs(inner, eventEmitter, preProcessingPhaseObjectGraphVisitors, typeConverters, NestedObjectSerializer) - ); - - traversalStrategy.Traverse(graph, emittingVisitor, emitter); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Serialization.Converters; +using YamlDotNet.Serialization.EventEmitters; +using YamlDotNet.Serialization.NamingConventions; +using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; +using YamlDotNet.Serialization.ObjectGraphVisitors; +using YamlDotNet.Serialization.TypeInspectors; +using YamlDotNet.Serialization.TypeResolvers; + +namespace YamlDotNet.Serialization +{ + + /// + /// Creates and configures instances of . + /// This class is used to customize the behavior of . Use the relevant methods + /// to apply customizations, then call to create an instance of the serializer + /// with the desired customizations. + /// + public sealed class SerializerBuilder : BuilderSkeleton + { + private ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory; + private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; + private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; + private readonly LazyComponentRegistrationList eventEmitterFactories; + private readonly IDictionary tagMappings = new Dictionary(); + private int maximumRecursion = 50; + private EmitterSettings emitterSettings = EmitterSettings.Default; + private DefaultValuesHandling defaultValuesHandlingConfiguration = DefaultValuesHandling.Preserve; + private bool quoteNecessaryStrings; + + public SerializerBuilder() + : base(new DynamicTypeResolver()) + { + typeInspectorFactories.Add(typeof(CachedTypeInspector), inner => new CachedTypeInspector(inner)); + typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), inner => namingConvention is NullNamingConvention ? inner : new NamingConventionTypeInspector(inner, namingConvention)); + typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), inner => new YamlAttributesTypeInspector(inner)); + typeInspectorFactories.Add(typeof(YamlAttributeOverridesInspector), inner => overrides != null ? new YamlAttributeOverridesInspector(inner, overrides.Clone()) : inner); + + preProcessingPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList, IObjectGraphVisitor> + { + { typeof(AnchorAssigner), typeConverters => new AnchorAssigner(typeConverters) } + }; + + emissionPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList> + { + { + typeof(CustomSerializationObjectGraphVisitor), + args => new CustomSerializationObjectGraphVisitor(args.InnerVisitor, args.TypeConverters, args.NestedObjectSerializer) + }, + { + typeof(AnchorAssigningObjectGraphVisitor), + args => new AnchorAssigningObjectGraphVisitor(args.InnerVisitor, args.EventEmitter, args.GetPreProcessingPhaseObjectGraphVisitor()) + }, + { + typeof(DefaultValuesObjectGraphVisitor), + args => new DefaultValuesObjectGraphVisitor(defaultValuesHandlingConfiguration, args.InnerVisitor) + }, + { + typeof(CommentsObjectGraphVisitor), + args => new CommentsObjectGraphVisitor(args.InnerVisitor) + } + }; + + eventEmitterFactories = new LazyComponentRegistrationList + { + { typeof(TypeAssigningEventEmitter), inner => new TypeAssigningEventEmitter(inner, false, tagMappings, quoteNecessaryStrings) } + }; + + objectGraphTraversalStrategyFactory = (typeInspector, typeResolver, typeConverters, maximumRecursion) => new FullObjectGraphTraversalStrategy(typeInspector, typeResolver, maximumRecursion, namingConvention); + } + + protected override SerializerBuilder Self { get { return this; } } + + /// + /// Put double quotes around strings that need it, for example Null, True, False, a number. This should be called before any other "With" methods if you want this feature enabled. + /// + public SerializerBuilder WithQuotingNecessaryStrings() + { + quoteNecessaryStrings = true; + return this; + } + + /// + /// Sets the maximum recursion that is allowed while traversing the object graph. The default value is 50. + /// + public SerializerBuilder WithMaximumRecursion(int maximumRecursion) + { + if (maximumRecursion <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maximumRecursion), $"The maximum recursion specified ({maximumRecursion}) is invalid. It should be a positive integer."); + } + + this.maximumRecursion = maximumRecursion; + return this; + } + + /// + /// Registers an additional to be used by the serializer. + /// + /// A function that instantiates the event emitter. + public SerializerBuilder WithEventEmitter(Func eventEmitterFactory) + where TEventEmitter : IEventEmitter + { + return WithEventEmitter(eventEmitterFactory, w => w.OnTop()); + } + + /// + /// Registers an additional to be used by the serializer. + /// + /// A function that instantiates the event emitter. + /// Configures the location where to insert the + public SerializerBuilder WithEventEmitter( + Func eventEmitterFactory, + Action> where + ) + where TEventEmitter : IEventEmitter + { + if (eventEmitterFactory == null) + { + throw new ArgumentNullException(nameof(eventEmitterFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(eventEmitterFactories.CreateRegistrationLocationSelector(typeof(TEventEmitter), inner => eventEmitterFactory(inner))); + return Self; + } + + /// + /// Registers an additional to be used by the serializer. + /// + /// A function that instantiates the event emitter based on a previously registered . + /// Configures the location where to insert the + public SerializerBuilder WithEventEmitter( + WrapperFactory eventEmitterFactory, + Action> where + ) + where TEventEmitter : IEventEmitter + { + if (eventEmitterFactory == null) + { + throw new ArgumentNullException(nameof(eventEmitterFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(eventEmitterFactories.CreateTrackingRegistrationLocationSelector(typeof(TEventEmitter), (wrapped, inner) => eventEmitterFactory(wrapped, inner))); + return Self; + } + + /// + /// Unregisters an existing of type . + /// + public SerializerBuilder WithoutEventEmitter() + where TEventEmitter : IEventEmitter + { + return WithoutEventEmitter(typeof(TEventEmitter)); + } + + /// + /// Unregisters an existing of type . + /// + public SerializerBuilder WithoutEventEmitter(Type eventEmitterType) + { + if (eventEmitterType == null) + { + throw new ArgumentNullException(nameof(eventEmitterType)); + } + + eventEmitterFactories.Remove(eventEmitterType); + return this; + } + + /// + /// Registers a tag mapping. + /// + public override SerializerBuilder WithTagMapping(TagName tag, Type type) + { + if (tag.IsEmpty) + { + throw new ArgumentException("Non-specific tags cannot be maped"); + } + + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (tagMappings.TryGetValue(type, out var alreadyRegisteredTag)) + { + throw new ArgumentException($"Type already has a registered tag '{alreadyRegisteredTag}' for type '{type.FullName}'", nameof(type)); + } + + tagMappings.Add(type, tag); + return this; + } + + /// + /// Unregisters an existing tag mapping. + /// + public SerializerBuilder WithoutTagMapping(Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (!tagMappings.Remove(type)) + { + throw new KeyNotFoundException($"Tag for type '{type.FullName}' is not registered"); + } + return this; + } + + /// + /// Ensures that it will be possible to deserialize the serialized objects. + /// This option will force the emission of tags and emit only properties with setters. + /// + public SerializerBuilder EnsureRoundtrip() + { + objectGraphTraversalStrategyFactory = (typeInspector, typeResolver, typeConverters, maximumRecursion) => new RoundtripObjectGraphTraversalStrategy( + typeConverters, + typeInspector, + typeResolver, + maximumRecursion, + namingConvention + ); + WithEventEmitter(inner => new TypeAssigningEventEmitter(inner, true, tagMappings, quoteNecessaryStrings), loc => loc.InsteadOf()); + return WithTypeInspector(inner => new ReadableAndWritablePropertiesTypeInspector(inner), loc => loc.OnBottom()); + } + + /// + /// Specifies that, if the same object appears more than once in the + /// serialization graph, it will be serialized each time instead of just once. + /// + /// + /// If the serialization graph contains circular references and this flag is set, + /// a StackOverflowException will be thrown. + /// If this flag is not set, there is a performance penalty because the entire + /// object graph must be walked twice. + /// + public SerializerBuilder DisableAliases() + { + preProcessingPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigner)); + emissionPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigningObjectGraphVisitor)); + return this; + } + + /// + /// Forces every value to be serialized, even if it is the default value for that type. + /// + [Obsolete("The default behavior is now to always emit default values, thefore calling this method has no effect. This behavior is now controlled by ConfigureDefaultValuesHandling.", error: true)] + public SerializerBuilder EmitDefaults() => ConfigureDefaultValuesHandling(DefaultValuesHandling.Preserve); + + /// + /// Configures how properties with default and null values should be handled. The default value is DefaultValuesHandling.Preserve + /// + /// + /// If more control is needed, create a class that extends from ChainedObjectGraphVisitor and override its EnterMapping methods. + /// Then register it as follows: + /// WithEmissionPhaseObjectGraphVisitor(args => new MyDefaultHandlingStrategy(args.InnerVisitor)); + /// + public SerializerBuilder ConfigureDefaultValuesHandling(DefaultValuesHandling configuration) + { + this.defaultValuesHandlingConfiguration = configuration; + return this; + } + + /// + /// Ensures that the result of the serialization is valid JSON. + /// + public SerializerBuilder JsonCompatible() + { + this.emitterSettings = this.emitterSettings + .WithMaxSimpleKeyLength(int.MaxValue) + .WithoutAnchorName(); + + return this + .WithTypeConverter(new GuidConverter(true), w => w.InsteadOf()) + .WithEventEmitter(inner => new JsonEventEmitter(inner), loc => loc.InsteadOf()); + } + + /// + /// Registers an additional to be used by the serializer + /// before emitting an object graph. + /// + /// + /// Registering a visitor in the pre-processing phase enables to traverse the object graph once + /// before actually emitting it. This allows a visitor to collect information about the graph that + /// can be used later by another visitor registered in the emission phase. + /// + /// The type inspector. + public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor) + where TObjectGraphVisitor : IObjectGraphVisitor + { + return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitor, w => w.OnTop()); + } + + /// + /// Registers an additional to be used by the serializer + /// before emitting an object graph. + /// + /// + /// Registering a visitor in the pre-processing phase enables to traverse the object graph once + /// before actually emitting it. This allows a visitor to collect information about the graph that + /// can be used later by another visitor registered in the emission phase. + /// + /// The type inspector. + /// Configures the location where to insert the + public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor( + TObjectGraphVisitor objectGraphVisitor, + Action>> where + ) + where TObjectGraphVisitor : IObjectGraphVisitor + { + if (objectGraphVisitor == null) + { + throw new ArgumentNullException(nameof(objectGraphVisitor)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), _ => objectGraphVisitor)); + return this; + } + + /// + /// Registers an additional to be used by the serializer + /// before emitting an object graph. + /// + /// + /// Registering a visitor in the pre-processing phase enables to traverse the object graph once + /// before actually emitting it. This allows a visitor to collect information about the graph that + /// can be used later by another visitor registered in the emission phase. + /// + /// A factory that creates the based on a previously registered . + /// Configures the location where to insert the + public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor( + WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, + Action>> where + ) + where TObjectGraphVisitor : IObjectGraphVisitor + { + if (objectGraphVisitorFactory == null) + { + throw new ArgumentNullException(nameof(objectGraphVisitorFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (wrapped, _) => objectGraphVisitorFactory(wrapped))); + return this; + } + + /// + /// Unregisters an existing of type . + /// + public SerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor() + where TObjectGraphVisitor : IObjectGraphVisitor + { + return WithoutPreProcessingPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); + } + + /// + /// Unregisters an existing of type . + /// + public SerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor(Type objectGraphVisitorType) + { + if (objectGraphVisitorType == null) + { + throw new ArgumentNullException(nameof(objectGraphVisitorType)); + } + + preProcessingPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); + return this; + } + + /// + /// Registers an to be used by the serializer + /// while traversing the object graph. + /// + /// A function that instantiates the traversal strategy. + public SerializerBuilder WithObjectGraphTraversalStrategyFactory(ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory) + { + this.objectGraphTraversalStrategyFactory = objectGraphTraversalStrategyFactory; + + return this; + } + + /// + /// Registers an additional to be used by the serializer + /// while emitting an object graph. + /// + /// A function that instantiates the type inspector. + public SerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory) + where TObjectGraphVisitor : IObjectGraphVisitor + { + return WithEmissionPhaseObjectGraphVisitor(objectGraphVisitorFactory, w => w.OnTop()); + } + + /// + /// Registers an additional to be used by the serializer + /// while emitting an object graph. + /// + /// A function that instantiates the type inspector. + /// Configures the location where to insert the + public SerializerBuilder WithEmissionPhaseObjectGraphVisitor( + Func objectGraphVisitorFactory, + Action>> where + ) + where TObjectGraphVisitor : IObjectGraphVisitor + { + if (objectGraphVisitorFactory == null) + { + throw new ArgumentNullException(nameof(objectGraphVisitorFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(emissionPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), args => objectGraphVisitorFactory(args))); + return this; + } + + /// + /// Registers an additional to be used by the serializer + /// while emitting an object graph. + /// + /// A function that instantiates the type inspector based on a previously registered . + /// Configures the location where to insert the + public SerializerBuilder WithEmissionPhaseObjectGraphVisitor( + WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, + Action>> where + ) + where TObjectGraphVisitor : IObjectGraphVisitor + { + if (objectGraphVisitorFactory == null) + { + throw new ArgumentNullException(nameof(objectGraphVisitorFactory)); + } + + if (where == null) + { + throw new ArgumentNullException(nameof(where)); + } + + where(emissionPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (wrapped, args) => objectGraphVisitorFactory(wrapped, args))); + return this; + } + + /// + /// Unregisters an existing of type . + /// + public SerializerBuilder WithoutEmissionPhaseObjectGraphVisitor() + where TObjectGraphVisitor : IObjectGraphVisitor + { + return WithoutEmissionPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); + } + + /// + /// Unregisters an existing of type . + /// + public SerializerBuilder WithoutEmissionPhaseObjectGraphVisitor(Type objectGraphVisitorType) + { + if (objectGraphVisitorType == null) + { + throw new ArgumentNullException(nameof(objectGraphVisitorType)); + } + + emissionPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); + return this; + } + + /// + /// Creates sequences with extra indentation + /// + /// + /// list: + /// - item + /// - item + /// + /// + public SerializerBuilder WithIndentedSequences() + { + emitterSettings = emitterSettings.WithIndentedSequences(); + + return this; + } + + /// + /// Creates a new according to the current configuration. + /// + public ISerializer Build() + { + return Serializer.FromValueSerializer(BuildValueSerializer(), emitterSettings); + } + + /// + /// Creates a new that implements the current configuration. + /// This method is available for advanced scenarios. The preferred way to customize the behavior of the + /// deserializer is to use the method. + /// + public IValueSerializer BuildValueSerializer() + { + var typeConverters = BuildTypeConverters(); + var typeInspector = BuildTypeInspector(); + var traversalStrategy = objectGraphTraversalStrategyFactory(typeInspector, typeResolver, typeConverters, maximumRecursion); + var eventEmitter = eventEmitterFactories.BuildComponentChain(new WriterEventEmitter()); + + return new ValueSerializer( + traversalStrategy, + eventEmitter, + typeConverters, + preProcessingPhaseObjectGraphVisitorFactories.Clone(), + emissionPhaseObjectGraphVisitorFactories.Clone() + ); + } + + private class ValueSerializer : IValueSerializer + { + private readonly IObjectGraphTraversalStrategy traversalStrategy; + private readonly IEventEmitter eventEmitter; + private readonly IEnumerable typeConverters; + private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; + private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; + + public ValueSerializer( + IObjectGraphTraversalStrategy traversalStrategy, + IEventEmitter eventEmitter, + IEnumerable typeConverters, + LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories, + LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories + ) + { + this.traversalStrategy = traversalStrategy; + this.eventEmitter = eventEmitter; + this.typeConverters = typeConverters; + this.preProcessingPhaseObjectGraphVisitorFactories = preProcessingPhaseObjectGraphVisitorFactories; + this.emissionPhaseObjectGraphVisitorFactories = emissionPhaseObjectGraphVisitorFactories; + } + + public void SerializeValue(IEmitter emitter, object? value, Type? type) + { + var actualType = type ?? (value != null ? value.GetType() : typeof(object)); + var staticType = type ?? typeof(object); + + var graph = new ObjectDescriptor(value, actualType, staticType); + + var preProcessingPhaseObjectGraphVisitors = preProcessingPhaseObjectGraphVisitorFactories.BuildComponentList(typeConverters); + foreach (var visitor in preProcessingPhaseObjectGraphVisitors) + { + traversalStrategy.Traverse(graph, visitor, default); + } + + void NestedObjectSerializer(object? v, Type? t) => SerializeValue(emitter, v, t); + + var emittingVisitor = emissionPhaseObjectGraphVisitorFactories.BuildComponentChain( + new EmittingObjectGraphVisitor(eventEmitter), + inner => new EmissionPhaseObjectGraphVisitorArgs(inner, eventEmitter, preProcessingPhaseObjectGraphVisitors, typeConverters, NestedObjectSerializer) + ); + + traversalStrategy.Traverse(graph, emittingVisitor, emitter); + } + } + } +} diff --git a/YamlDotNet/Serialization/StreamFragment.cs b/YamlDotNet/Serialization/StreamFragment.cs index eeb976e24..12d9de3ef 100644 --- a/YamlDotNet/Serialization/StreamFragment.cs +++ b/YamlDotNet/Serialization/StreamFragment.cs @@ -1,85 +1,85 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; - -namespace YamlDotNet.Serialization -{ - /// - /// An object that contains part of a YAML stream. - /// - public sealed class StreamFragment : IYamlConvertible - { - private readonly List events = new List(); - - /// - /// Gets or sets the events. - /// - /// The events. - public IList Events - { - get - { - return events; - } - } - - #region IYamlConvertible Members - /// - /// Reads this object's state from a YAML parser. - /// - void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) - { - events.Clear(); - - var depth = 0; - do - { - if (!parser.MoveNext()) - { - throw new InvalidOperationException("The parser has reached the end before deserialization completed."); - } - - var current = parser.Current!; - events.Add(current); - depth += current.NestingIncrease; - } while (depth > 0); - - Debug.Assert(depth == 0); - } - - /// - /// Writes this object's state to a YAML emitter. - /// - void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) - { - foreach (var item in events) - { - emitter.Emit(item); - } - } - #endregion - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization +{ + /// + /// An object that contains part of a YAML stream. + /// + public sealed class StreamFragment : IYamlConvertible + { + private readonly List events = new List(); + + /// + /// Gets or sets the events. + /// + /// The events. + public IList Events + { + get + { + return events; + } + } + + #region IYamlConvertible Members + /// + /// Reads this object's state from a YAML parser. + /// + void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) + { + events.Clear(); + + var depth = 0; + do + { + if (!parser.MoveNext()) + { + throw new InvalidOperationException("The parser has reached the end before deserialization completed."); + } + + var current = parser.Current!; + events.Add(current); + depth += current.NestingIncrease; + } while (depth > 0); + + Debug.Assert(depth == 0); + } + + /// + /// Writes this object's state to a YAML emitter. + /// + void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) + { + foreach (var item in events) + { + emitter.Emit(item); + } + } + #endregion + } +} diff --git a/YamlDotNet/Serialization/TagMappings.cs b/YamlDotNet/Serialization/TagMappings.cs index 2a774c82a..1792ea8d9 100644 --- a/YamlDotNet/Serialization/TagMappings.cs +++ b/YamlDotNet/Serialization/TagMappings.cs @@ -1,71 +1,71 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; - -namespace YamlDotNet.Serialization -{ - /// - /// Contains mappings between tags and types. - /// - public sealed class TagMappings - { - private readonly IDictionary mappings; - - /// - /// Initializes a new instance of the class. - /// - public TagMappings() - { - mappings = new Dictionary(); - } - - /// - /// Initializes a new instance of the class. - /// - /// The mappings. - public TagMappings(IDictionary mappings) - { - this.mappings = new Dictionary(mappings); - } - - /// - /// Adds the specified tag. - /// - /// The tag. - /// The mapping. - public void Add(string tag, Type mapping) - { - mappings.Add(tag, mapping); - } - - /// - /// Gets the mapping. - /// - /// The tag. - /// - internal Type? GetMapping(string tag) - { - return mappings.TryGetValue(tag, out var mapping) ? mapping : null; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace YamlDotNet.Serialization +{ + /// + /// Contains mappings between tags and types. + /// + public sealed class TagMappings + { + private readonly IDictionary mappings; + + /// + /// Initializes a new instance of the class. + /// + public TagMappings() + { + mappings = new Dictionary(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The mappings. + public TagMappings(IDictionary mappings) + { + this.mappings = new Dictionary(mappings); + } + + /// + /// Adds the specified tag. + /// + /// The tag. + /// The mapping. + public void Add(string tag, Type mapping) + { + mappings.Add(tag, mapping); + } + + /// + /// Gets the mapping. + /// + /// The tag. + /// + internal Type? GetMapping(string tag) + { + return mappings.TryGetValue(tag, out var mapping) ? mapping : null; + } + } +} diff --git a/YamlDotNet/Serialization/TypeInspectors/CachedTypeInspector.cs b/YamlDotNet/Serialization/TypeInspectors/CachedTypeInspector.cs index 75897db6d..727b8f193 100644 --- a/YamlDotNet/Serialization/TypeInspectors/CachedTypeInspector.cs +++ b/YamlDotNet/Serialization/TypeInspectors/CachedTypeInspector.cs @@ -1,47 +1,47 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; - -namespace YamlDotNet.Serialization.TypeInspectors -{ - /// - /// Wraps another and applies caching. - /// - public sealed class CachedTypeInspector : TypeInspectorSkeleton - { - private readonly ITypeInspector innerTypeDescriptor; - private readonly ConcurrentDictionary> cache = new ConcurrentDictionary>(); - - public CachedTypeInspector(ITypeInspector innerTypeDescriptor) - { - this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException(nameof(innerTypeDescriptor)); - } - - public override IEnumerable GetProperties(Type type, object? container) - { - return cache.GetOrAdd(type, t => innerTypeDescriptor.GetProperties(t, container).ToList()); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace YamlDotNet.Serialization.TypeInspectors +{ + /// + /// Wraps another and applies caching. + /// + public sealed class CachedTypeInspector : TypeInspectorSkeleton + { + private readonly ITypeInspector innerTypeDescriptor; + private readonly ConcurrentDictionary> cache = new ConcurrentDictionary>(); + + public CachedTypeInspector(ITypeInspector innerTypeDescriptor) + { + this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException(nameof(innerTypeDescriptor)); + } + + public override IEnumerable GetProperties(Type type, object? container) + { + return cache.GetOrAdd(type, t => innerTypeDescriptor.GetProperties(t, container).ToList()); + } + } +} diff --git a/YamlDotNet/Serialization/TypeInspectors/CompositeTypeInspector.cs b/YamlDotNet/Serialization/TypeInspectors/CompositeTypeInspector.cs index 58bd49a71..825cc9953 100644 --- a/YamlDotNet/Serialization/TypeInspectors/CompositeTypeInspector.cs +++ b/YamlDotNet/Serialization/TypeInspectors/CompositeTypeInspector.cs @@ -1,51 +1,51 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace YamlDotNet.Serialization.TypeInspectors -{ - /// - /// Aggregates the results from multiple into a single one. - /// - public sealed class CompositeTypeInspector : TypeInspectorSkeleton - { - private readonly IEnumerable typeInspectors; - - public CompositeTypeInspector(params ITypeInspector[] typeInspectors) - : this((IEnumerable)typeInspectors) - { - } - - public CompositeTypeInspector(IEnumerable typeInspectors) - { - this.typeInspectors = typeInspectors?.ToList() ?? throw new ArgumentNullException(nameof(typeInspectors)); - } - - public override IEnumerable GetProperties(Type type, object? container) - { - return typeInspectors - .SelectMany(i => i.GetProperties(type, container)); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace YamlDotNet.Serialization.TypeInspectors +{ + /// + /// Aggregates the results from multiple into a single one. + /// + public sealed class CompositeTypeInspector : TypeInspectorSkeleton + { + private readonly IEnumerable typeInspectors; + + public CompositeTypeInspector(params ITypeInspector[] typeInspectors) + : this((IEnumerable)typeInspectors) + { + } + + public CompositeTypeInspector(IEnumerable typeInspectors) + { + this.typeInspectors = typeInspectors?.ToList() ?? throw new ArgumentNullException(nameof(typeInspectors)); + } + + public override IEnumerable GetProperties(Type type, object? container) + { + return typeInspectors + .SelectMany(i => i.GetProperties(type, container)); + } + } +} diff --git a/YamlDotNet/Serialization/TypeInspectors/NamingConventionTypeInspector.cs b/YamlDotNet/Serialization/TypeInspectors/NamingConventionTypeInspector.cs index 16222270b..709b703f5 100644 --- a/YamlDotNet/Serialization/TypeInspectors/NamingConventionTypeInspector.cs +++ b/YamlDotNet/Serialization/TypeInspectors/NamingConventionTypeInspector.cs @@ -1,57 +1,57 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace YamlDotNet.Serialization.TypeInspectors -{ - /// - /// Wraps another and applies a - /// naming convention to the names of the properties. - /// - public sealed class NamingConventionTypeInspector : TypeInspectorSkeleton - { - private readonly ITypeInspector innerTypeDescriptor; - private readonly INamingConvention namingConvention; - - public NamingConventionTypeInspector(ITypeInspector innerTypeDescriptor, INamingConvention namingConvention) - { - this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException(nameof(innerTypeDescriptor)); - this.namingConvention = namingConvention ?? throw new ArgumentNullException(nameof(namingConvention)); - } - - public override IEnumerable GetProperties(Type type, object? container) - { - return innerTypeDescriptor.GetProperties(type, container) - .Select(p => - { - var attribute = p.GetCustomAttribute(); - if (attribute != null && !attribute.ApplyNamingConventions) - { - return p; - } - return new PropertyDescriptor(p) { Name = namingConvention.Apply(p.Name) }; - }); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace YamlDotNet.Serialization.TypeInspectors +{ + /// + /// Wraps another and applies a + /// naming convention to the names of the properties. + /// + public sealed class NamingConventionTypeInspector : TypeInspectorSkeleton + { + private readonly ITypeInspector innerTypeDescriptor; + private readonly INamingConvention namingConvention; + + public NamingConventionTypeInspector(ITypeInspector innerTypeDescriptor, INamingConvention namingConvention) + { + this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException(nameof(innerTypeDescriptor)); + this.namingConvention = namingConvention ?? throw new ArgumentNullException(nameof(namingConvention)); + } + + public override IEnumerable GetProperties(Type type, object? container) + { + return innerTypeDescriptor.GetProperties(type, container) + .Select(p => + { + var attribute = p.GetCustomAttribute(); + if (attribute != null && !attribute.ApplyNamingConventions) + { + return p; + } + return new PropertyDescriptor(p) { Name = namingConvention.Apply(p.Name) }; + }); + } + } +} diff --git a/YamlDotNet/Serialization/TypeInspectors/ReadableAndWritablePropertiesTypeInspector.cs b/YamlDotNet/Serialization/TypeInspectors/ReadableAndWritablePropertiesTypeInspector.cs index 158ae5a81..22921e1fa 100644 --- a/YamlDotNet/Serialization/TypeInspectors/ReadableAndWritablePropertiesTypeInspector.cs +++ b/YamlDotNet/Serialization/TypeInspectors/ReadableAndWritablePropertiesTypeInspector.cs @@ -1,46 +1,46 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace YamlDotNet.Serialization.TypeInspectors -{ - /// - /// Returns the properties of a type that are both readable and writable. - /// - public sealed class ReadableAndWritablePropertiesTypeInspector : TypeInspectorSkeleton - { - private readonly ITypeInspector innerTypeDescriptor; - - public ReadableAndWritablePropertiesTypeInspector(ITypeInspector innerTypeDescriptor) - { - this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException(nameof(innerTypeDescriptor)); - } - - public override IEnumerable GetProperties(Type type, object? container) - { - return innerTypeDescriptor.GetProperties(type, container) - .Where(p => p.CanWrite); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace YamlDotNet.Serialization.TypeInspectors +{ + /// + /// Returns the properties of a type that are both readable and writable. + /// + public sealed class ReadableAndWritablePropertiesTypeInspector : TypeInspectorSkeleton + { + private readonly ITypeInspector innerTypeDescriptor; + + public ReadableAndWritablePropertiesTypeInspector(ITypeInspector innerTypeDescriptor) + { + this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException(nameof(innerTypeDescriptor)); + } + + public override IEnumerable GetProperties(Type type, object? container) + { + return innerTypeDescriptor.GetProperties(type, container) + .Where(p => p.CanWrite); + } + } +} diff --git a/YamlDotNet/Serialization/TypeInspectors/ReadableFieldsTypeInspector.cs b/YamlDotNet/Serialization/TypeInspectors/ReadableFieldsTypeInspector.cs index b45a97079..9e6186877 100644 --- a/YamlDotNet/Serialization/TypeInspectors/ReadableFieldsTypeInspector.cs +++ b/YamlDotNet/Serialization/TypeInspectors/ReadableFieldsTypeInspector.cs @@ -1,87 +1,87 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.TypeInspectors -{ - /// - /// Returns the properties and fields of a type that are readable. - /// - public sealed class ReadableFieldsTypeInspector : TypeInspectorSkeleton - { - private readonly ITypeResolver typeResolver; - - public ReadableFieldsTypeInspector(ITypeResolver typeResolver) - { - this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); - } - - public override IEnumerable GetProperties(Type type, object? container) - { - return type - .GetPublicFields() - .Select(p => (IPropertyDescriptor)new ReflectionFieldDescriptor(p, typeResolver)); - } - - private sealed class ReflectionFieldDescriptor : IPropertyDescriptor - { - private readonly FieldInfo fieldInfo; - private readonly ITypeResolver typeResolver; - - public ReflectionFieldDescriptor(FieldInfo fieldInfo, ITypeResolver typeResolver) - { - this.fieldInfo = fieldInfo; - this.typeResolver = typeResolver; - ScalarStyle = ScalarStyle.Any; - } - - public string Name { get { return fieldInfo.Name; } } - public Type Type { get { return fieldInfo.FieldType; } } - public Type? TypeOverride { get; set; } - public int Order { get; set; } - public bool CanWrite { get { return !fieldInfo.IsInitOnly; } } - public ScalarStyle ScalarStyle { get; set; } - - public void Write(object target, object? value) - { - fieldInfo.SetValue(target, value); - } - - public T GetCustomAttribute() where T : Attribute - { - var attributes = fieldInfo.GetCustomAttributes(typeof(T), true); - return (T)attributes.FirstOrDefault(); - } - - public IObjectDescriptor Read(object target) - { - var propertyValue = fieldInfo.GetValue(target); - var actualType = TypeOverride ?? typeResolver.Resolve(Type, propertyValue); - return new ObjectDescriptor(propertyValue, actualType, Type, ScalarStyle); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.TypeInspectors +{ + /// + /// Returns the properties and fields of a type that are readable. + /// + public sealed class ReadableFieldsTypeInspector : TypeInspectorSkeleton + { + private readonly ITypeResolver typeResolver; + + public ReadableFieldsTypeInspector(ITypeResolver typeResolver) + { + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); + } + + public override IEnumerable GetProperties(Type type, object? container) + { + return type + .GetPublicFields() + .Select(p => (IPropertyDescriptor)new ReflectionFieldDescriptor(p, typeResolver)); + } + + private sealed class ReflectionFieldDescriptor : IPropertyDescriptor + { + private readonly FieldInfo fieldInfo; + private readonly ITypeResolver typeResolver; + + public ReflectionFieldDescriptor(FieldInfo fieldInfo, ITypeResolver typeResolver) + { + this.fieldInfo = fieldInfo; + this.typeResolver = typeResolver; + ScalarStyle = ScalarStyle.Any; + } + + public string Name { get { return fieldInfo.Name; } } + public Type Type { get { return fieldInfo.FieldType; } } + public Type? TypeOverride { get; set; } + public int Order { get; set; } + public bool CanWrite { get { return !fieldInfo.IsInitOnly; } } + public ScalarStyle ScalarStyle { get; set; } + + public void Write(object target, object? value) + { + fieldInfo.SetValue(target, value); + } + + public T GetCustomAttribute() where T : Attribute + { + var attributes = fieldInfo.GetCustomAttributes(typeof(T), true); + return (T)attributes.FirstOrDefault(); + } + + public IObjectDescriptor Read(object target) + { + var propertyValue = fieldInfo.GetValue(target); + var actualType = TypeOverride ?? typeResolver.Resolve(Type, propertyValue); + return new ObjectDescriptor(propertyValue, actualType, Type, ScalarStyle); + } + } + } +} diff --git a/YamlDotNet/Serialization/TypeInspectors/ReadablePropertiesTypeInspector.cs b/YamlDotNet/Serialization/TypeInspectors/ReadablePropertiesTypeInspector.cs index 12fe116ee..25484c97a 100644 --- a/YamlDotNet/Serialization/TypeInspectors/ReadablePropertiesTypeInspector.cs +++ b/YamlDotNet/Serialization/TypeInspectors/ReadablePropertiesTypeInspector.cs @@ -1,101 +1,101 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.TypeInspectors -{ - /// - /// Returns the properties of a type that are readable. - /// - public sealed class ReadablePropertiesTypeInspector : TypeInspectorSkeleton - { - private readonly ITypeResolver typeResolver; - private readonly bool includeNonPublicProperties; - - public ReadablePropertiesTypeInspector(ITypeResolver typeResolver) - : this(typeResolver, false) - { - } - - public ReadablePropertiesTypeInspector(ITypeResolver typeResolver, bool includeNonPublicProperties) - { - this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); - this.includeNonPublicProperties = includeNonPublicProperties; - } - - private static bool IsValidProperty(PropertyInfo property) - { - return property.CanRead - && property.GetGetMethod(true)!.GetParameters().Length == 0; - } - - public override IEnumerable GetProperties(Type type, object? container) - { - return type - .GetProperties(includeNonPublicProperties) - .Where(IsValidProperty) - .Select(p => (IPropertyDescriptor)new ReflectionPropertyDescriptor(p, typeResolver)); - } - - private sealed class ReflectionPropertyDescriptor : IPropertyDescriptor - { - private readonly PropertyInfo propertyInfo; - private readonly ITypeResolver typeResolver; - - public ReflectionPropertyDescriptor(PropertyInfo propertyInfo, ITypeResolver typeResolver) - { - this.propertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo)); - this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); - ScalarStyle = ScalarStyle.Any; - } - - public string Name => propertyInfo.Name; - public Type Type => propertyInfo.PropertyType; - public Type? TypeOverride { get; set; } - public int Order { get; set; } - public bool CanWrite => propertyInfo.CanWrite; - public ScalarStyle ScalarStyle { get; set; } - - public void Write(object target, object? value) - { - propertyInfo.SetValue(target, value, null); - } - - public T GetCustomAttribute() where T : Attribute - { - var attributes = propertyInfo.GetAllCustomAttributes(); - return (T)attributes.FirstOrDefault(); - } - - public IObjectDescriptor Read(object target) - { - var propertyValue = propertyInfo.ReadValue(target); - var actualType = TypeOverride ?? typeResolver.Resolve(Type, propertyValue); - return new ObjectDescriptor(propertyValue, actualType, Type, ScalarStyle); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.TypeInspectors +{ + /// + /// Returns the properties of a type that are readable. + /// + public sealed class ReadablePropertiesTypeInspector : TypeInspectorSkeleton + { + private readonly ITypeResolver typeResolver; + private readonly bool includeNonPublicProperties; + + public ReadablePropertiesTypeInspector(ITypeResolver typeResolver) + : this(typeResolver, false) + { + } + + public ReadablePropertiesTypeInspector(ITypeResolver typeResolver, bool includeNonPublicProperties) + { + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); + this.includeNonPublicProperties = includeNonPublicProperties; + } + + private static bool IsValidProperty(PropertyInfo property) + { + return property.CanRead + && property.GetGetMethod(true)!.GetParameters().Length == 0; + } + + public override IEnumerable GetProperties(Type type, object? container) + { + return type + .GetProperties(includeNonPublicProperties) + .Where(IsValidProperty) + .Select(p => (IPropertyDescriptor)new ReflectionPropertyDescriptor(p, typeResolver)); + } + + private sealed class ReflectionPropertyDescriptor : IPropertyDescriptor + { + private readonly PropertyInfo propertyInfo; + private readonly ITypeResolver typeResolver; + + public ReflectionPropertyDescriptor(PropertyInfo propertyInfo, ITypeResolver typeResolver) + { + this.propertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo)); + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); + ScalarStyle = ScalarStyle.Any; + } + + public string Name => propertyInfo.Name; + public Type Type => propertyInfo.PropertyType; + public Type? TypeOverride { get; set; } + public int Order { get; set; } + public bool CanWrite => propertyInfo.CanWrite; + public ScalarStyle ScalarStyle { get; set; } + + public void Write(object target, object? value) + { + propertyInfo.SetValue(target, value, null); + } + + public T GetCustomAttribute() where T : Attribute + { + var attributes = propertyInfo.GetAllCustomAttributes(); + return (T)attributes.FirstOrDefault(); + } + + public IObjectDescriptor Read(object target) + { + var propertyValue = propertyInfo.ReadValue(target); + var actualType = TypeOverride ?? typeResolver.Resolve(Type, propertyValue); + return new ObjectDescriptor(propertyValue, actualType, Type, ScalarStyle); + } + } + } +} diff --git a/YamlDotNet/Serialization/TypeInspectors/TypeInspectorSkeleton.cs b/YamlDotNet/Serialization/TypeInspectors/TypeInspectorSkeleton.cs index d062d2cce..ea9e00bb8 100644 --- a/YamlDotNet/Serialization/TypeInspectors/TypeInspectorSkeleton.cs +++ b/YamlDotNet/Serialization/TypeInspectors/TypeInspectorSkeleton.cs @@ -1,62 +1,62 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.Serialization; - -namespace YamlDotNet.Serialization.TypeInspectors -{ - public abstract class TypeInspectorSkeleton : ITypeInspector - { - public abstract IEnumerable GetProperties(Type type, object? container); - - public IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched) - { - var candidates = GetProperties(type, container) - .Where(p => p.Name == name); - - using var enumerator = candidates.GetEnumerator(); - if (!enumerator.MoveNext()) - { - if (ignoreUnmatched) - { - return null!; - } - - throw new SerializationException($"Property '{name}' not found on type '{type.FullName}'."); - } - - var property = enumerator.Current; - - if (enumerator.MoveNext()) - { - throw new SerializationException( - $"Multiple properties with the name/alias '{name}' already exists on type '{type.FullName}', maybe you're misusing YamlAlias or maybe you are using the wrong naming convention? The matching properties are: {string.Join(", ", candidates.Select(p => p.Name).ToArray())}" - ); - } - - return property; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.Serialization; + +namespace YamlDotNet.Serialization.TypeInspectors +{ + public abstract class TypeInspectorSkeleton : ITypeInspector + { + public abstract IEnumerable GetProperties(Type type, object? container); + + public IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched) + { + var candidates = GetProperties(type, container) + .Where(p => p.Name == name); + + using var enumerator = candidates.GetEnumerator(); + if (!enumerator.MoveNext()) + { + if (ignoreUnmatched) + { + return null!; + } + + throw new SerializationException($"Property '{name}' not found on type '{type.FullName}'."); + } + + var property = enumerator.Current; + + if (enumerator.MoveNext()) + { + throw new SerializationException( + $"Multiple properties with the name/alias '{name}' already exists on type '{type.FullName}', maybe you're misusing YamlAlias or maybe you are using the wrong naming convention? The matching properties are: {string.Join(", ", candidates.Select(p => p.Name).ToArray())}" + ); + } + + return property; + } + } +} diff --git a/YamlDotNet/Serialization/TypeInspectors/WritablePropertiesTypeInspector.cs b/YamlDotNet/Serialization/TypeInspectors/WritablePropertiesTypeInspector.cs index a4b227f9a..ebb8967b4 100644 --- a/YamlDotNet/Serialization/TypeInspectors/WritablePropertiesTypeInspector.cs +++ b/YamlDotNet/Serialization/TypeInspectors/WritablePropertiesTypeInspector.cs @@ -1,102 +1,102 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.TypeInspectors -{ - /// - /// Returns the properties of a type that are writable. - /// - public sealed class WritablePropertiesTypeInspector : TypeInspectorSkeleton - { - private readonly ITypeResolver typeResolver; - private readonly bool includeNonPublicProperties; - - public WritablePropertiesTypeInspector(ITypeResolver typeResolver) - : this(typeResolver, false) - { - } - - public WritablePropertiesTypeInspector(ITypeResolver typeResolver, bool includeNonPublicProperties) - { - this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); - this.includeNonPublicProperties = includeNonPublicProperties; - } - - private static bool IsValidProperty(PropertyInfo property) - { - return property.CanWrite - && property.GetSetMethod(true)!.GetParameters().Length == 1; - } - - public override IEnumerable GetProperties(Type type, object? container) - { - return type - .GetProperties(includeNonPublicProperties) - .Where(IsValidProperty) - .Select(p => (IPropertyDescriptor)new ReflectionPropertyDescriptor(p, typeResolver)) - .ToArray(); - } - - private sealed class ReflectionPropertyDescriptor : IPropertyDescriptor - { - private readonly PropertyInfo propertyInfo; - private readonly ITypeResolver typeResolver; - - public ReflectionPropertyDescriptor(PropertyInfo propertyInfo, ITypeResolver typeResolver) - { - this.propertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo)); - this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); - ScalarStyle = ScalarStyle.Any; - } - - public string Name => propertyInfo.Name; - public Type Type => propertyInfo.PropertyType; - public Type? TypeOverride { get; set; } - public int Order { get; set; } - public bool CanWrite => propertyInfo.CanWrite; - public ScalarStyle ScalarStyle { get; set; } - - public void Write(object target, object? value) - { - propertyInfo.SetValue(target, value, null); - } - - public T GetCustomAttribute() where T : Attribute - { - var attributes = propertyInfo.GetAllCustomAttributes(); - return (T)attributes.FirstOrDefault(); - } - - public IObjectDescriptor Read(object target) - { - var propertyValue = propertyInfo.ReadValue(target); - var actualType = TypeOverride ?? typeResolver.Resolve(Type, propertyValue); - return new ObjectDescriptor(propertyValue, actualType, Type, ScalarStyle); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.TypeInspectors +{ + /// + /// Returns the properties of a type that are writable. + /// + public sealed class WritablePropertiesTypeInspector : TypeInspectorSkeleton + { + private readonly ITypeResolver typeResolver; + private readonly bool includeNonPublicProperties; + + public WritablePropertiesTypeInspector(ITypeResolver typeResolver) + : this(typeResolver, false) + { + } + + public WritablePropertiesTypeInspector(ITypeResolver typeResolver, bool includeNonPublicProperties) + { + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); + this.includeNonPublicProperties = includeNonPublicProperties; + } + + private static bool IsValidProperty(PropertyInfo property) + { + return property.CanWrite + && property.GetSetMethod(true)!.GetParameters().Length == 1; + } + + public override IEnumerable GetProperties(Type type, object? container) + { + return type + .GetProperties(includeNonPublicProperties) + .Where(IsValidProperty) + .Select(p => (IPropertyDescriptor)new ReflectionPropertyDescriptor(p, typeResolver)) + .ToArray(); + } + + private sealed class ReflectionPropertyDescriptor : IPropertyDescriptor + { + private readonly PropertyInfo propertyInfo; + private readonly ITypeResolver typeResolver; + + public ReflectionPropertyDescriptor(PropertyInfo propertyInfo, ITypeResolver typeResolver) + { + this.propertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo)); + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); + ScalarStyle = ScalarStyle.Any; + } + + public string Name => propertyInfo.Name; + public Type Type => propertyInfo.PropertyType; + public Type? TypeOverride { get; set; } + public int Order { get; set; } + public bool CanWrite => propertyInfo.CanWrite; + public ScalarStyle ScalarStyle { get; set; } + + public void Write(object target, object? value) + { + propertyInfo.SetValue(target, value, null); + } + + public T GetCustomAttribute() where T : Attribute + { + var attributes = propertyInfo.GetAllCustomAttributes(); + return (T)attributes.FirstOrDefault(); + } + + public IObjectDescriptor Read(object target) + { + var propertyValue = propertyInfo.ReadValue(target); + var actualType = TypeOverride ?? typeResolver.Resolve(Type, propertyValue); + return new ObjectDescriptor(propertyValue, actualType, Type, ScalarStyle); + } + } + } +} diff --git a/YamlDotNet/Serialization/TypeResolvers/DynamicTypeResolver.cs b/YamlDotNet/Serialization/TypeResolvers/DynamicTypeResolver.cs index 1be864afe..dcf636a3a 100644 --- a/YamlDotNet/Serialization/TypeResolvers/DynamicTypeResolver.cs +++ b/YamlDotNet/Serialization/TypeResolvers/DynamicTypeResolver.cs @@ -1,36 +1,36 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Serialization.TypeResolvers -{ - /// - /// The type returned will be the actual type of the value, if available. - /// - public sealed class DynamicTypeResolver : ITypeResolver - { - public Type Resolve(Type staticType, object? actualValue) - { - return actualValue != null ? actualValue.GetType() : staticType; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Serialization.TypeResolvers +{ + /// + /// The type returned will be the actual type of the value, if available. + /// + public sealed class DynamicTypeResolver : ITypeResolver + { + public Type Resolve(Type staticType, object? actualValue) + { + return actualValue != null ? actualValue.GetType() : staticType; + } + } +} diff --git a/YamlDotNet/Serialization/TypeResolvers/StaticTypeResolver.cs b/YamlDotNet/Serialization/TypeResolvers/StaticTypeResolver.cs index 36902a247..7bd96bc6c 100644 --- a/YamlDotNet/Serialization/TypeResolvers/StaticTypeResolver.cs +++ b/YamlDotNet/Serialization/TypeResolvers/StaticTypeResolver.cs @@ -1,36 +1,36 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Serialization.TypeResolvers -{ - /// - /// The type returned will always be the static type. - /// - public sealed class StaticTypeResolver : ITypeResolver - { - public Type Resolve(Type staticType, object? actualValue) - { - return staticType; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Serialization.TypeResolvers +{ + /// + /// The type returned will always be the static type. + /// + public sealed class StaticTypeResolver : ITypeResolver + { + public Type Resolve(Type staticType, object? actualValue) + { + return staticType; + } + } +} diff --git a/YamlDotNet/Serialization/Utilities/IPostDeserializationCallback.cs b/YamlDotNet/Serialization/Utilities/IPostDeserializationCallback.cs index 4358c295b..5f8c483d2 100644 --- a/YamlDotNet/Serialization/Utilities/IPostDeserializationCallback.cs +++ b/YamlDotNet/Serialization/Utilities/IPostDeserializationCallback.cs @@ -1,32 +1,32 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace YamlDotNet.Serialization.Utilities -{ - /// - /// Indicates that a class used as deserialization state - /// needs to be notified after deserialization. - /// - public interface IPostDeserializationCallback - { - void OnDeserialization(); - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace YamlDotNet.Serialization.Utilities +{ + /// + /// Indicates that a class used as deserialization state + /// needs to be notified after deserialization. + /// + public interface IPostDeserializationCallback + { + void OnDeserialization(); + } +} diff --git a/YamlDotNet/Serialization/Utilities/ObjectAnchorCollection.cs b/YamlDotNet/Serialization/Utilities/ObjectAnchorCollection.cs index 708acbb40..1762bc50f 100644 --- a/YamlDotNet/Serialization/Utilities/ObjectAnchorCollection.cs +++ b/YamlDotNet/Serialization/Utilities/ObjectAnchorCollection.cs @@ -1,75 +1,75 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization.Utilities -{ - internal sealed class ObjectAnchorCollection - { - private readonly IDictionary objectsByAnchor = new Dictionary(); - private readonly IDictionary anchorsByObject = new Dictionary(); - - /// - /// Adds the specified anchor. - /// - /// The anchor. - /// The @object. - public void Add(string anchor, object @object) - { - objectsByAnchor.Add(anchor, @object); - if (@object != null) - { - anchorsByObject.Add(@object, anchor); - } - } - - /// - /// Gets the anchor for the specified object. - /// - /// The object. - /// The anchor. - /// - public bool TryGetAnchor(object @object, [MaybeNullWhen(false)] out string? anchor) - { - return anchorsByObject.TryGetValue(@object, out anchor); - } - - /// - /// Gets the with the specified anchor. - /// - /// - public object this[string anchor] - { - get - { - if (objectsByAnchor.TryGetValue(anchor, out var value)) - { - return value; - } - - throw new AnchorNotFoundException($"The anchor '{anchor}' does not exists"); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization.Utilities +{ + internal sealed class ObjectAnchorCollection + { + private readonly IDictionary objectsByAnchor = new Dictionary(); + private readonly IDictionary anchorsByObject = new Dictionary(); + + /// + /// Adds the specified anchor. + /// + /// The anchor. + /// The @object. + public void Add(string anchor, object @object) + { + objectsByAnchor.Add(anchor, @object); + if (@object != null) + { + anchorsByObject.Add(@object, anchor); + } + } + + /// + /// Gets the anchor for the specified object. + /// + /// The object. + /// The anchor. + /// + public bool TryGetAnchor(object @object, [MaybeNullWhen(false)] out string? anchor) + { + return anchorsByObject.TryGetValue(@object, out anchor); + } + + /// + /// Gets the with the specified anchor. + /// + /// + public object this[string anchor] + { + get + { + if (objectsByAnchor.TryGetValue(anchor, out var value)) + { + return value; + } + + throw new AnchorNotFoundException($"The anchor '{anchor}' does not exists"); + } + } + } +} diff --git a/YamlDotNet/Serialization/Utilities/ReflectionUtility.cs b/YamlDotNet/Serialization/Utilities/ReflectionUtility.cs index 0fd8d2e29..d993e0bba 100644 --- a/YamlDotNet/Serialization/Utilities/ReflectionUtility.cs +++ b/YamlDotNet/Serialization/Utilities/ReflectionUtility.cs @@ -1,54 +1,54 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; - -namespace YamlDotNet.Serialization.Utilities -{ - internal static class ReflectionUtility - { - public static Type? GetImplementedGenericInterface(Type type, Type genericInterfaceType) - { - foreach (var interfacetype in GetImplementedInterfaces(type)) - { - if (interfacetype.IsGenericType() && interfacetype.GetGenericTypeDefinition() == genericInterfaceType) - { - return interfacetype; - } - } - return null; - } - - public static IEnumerable GetImplementedInterfaces(Type type) - { - if (type.IsInterface()) - { - yield return type; - } - - foreach (var implementedInterface in type.GetInterfaces()) - { - yield return implementedInterface; - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace YamlDotNet.Serialization.Utilities +{ + internal static class ReflectionUtility + { + public static Type? GetImplementedGenericInterface(Type type, Type genericInterfaceType) + { + foreach (var interfacetype in GetImplementedInterfaces(type)) + { + if (interfacetype.IsGenericType() && interfacetype.GetGenericTypeDefinition() == genericInterfaceType) + { + return interfacetype; + } + } + return null; + } + + public static IEnumerable GetImplementedInterfaces(Type type) + { + if (type.IsInterface()) + { + yield return type; + } + + foreach (var implementedInterface in type.GetInterfaces()) + { + yield return implementedInterface; + } + } + } +} diff --git a/YamlDotNet/Serialization/Utilities/SerializerState.cs b/YamlDotNet/Serialization/Utilities/SerializerState.cs index f99a2653a..085af20cc 100644 --- a/YamlDotNet/Serialization/Utilities/SerializerState.cs +++ b/YamlDotNet/Serialization/Utilities/SerializerState.cs @@ -1,67 +1,67 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace YamlDotNet.Serialization.Utilities -{ - /// - /// A generic container that is preserved during the entire deserialization process. - /// Any disposable object added to this collection will be disposed when this object is disposed. - /// - public sealed class SerializerState : IDisposable - { - private readonly IDictionary items = new Dictionary(); - - public T Get() - where T : class, new() - { - if (!items.TryGetValue(typeof(T), out var value)) - { - value = new T(); - items.Add(typeof(T), value); - } - return (T)value; - } - - /// - /// Invokes on all - /// objects added to this collection that implement . - /// - public void OnDeserialization() - { - foreach (var callback in items.Values.OfType()) - { - callback.OnDeserialization(); - } - } - - public void Dispose() - { - foreach (var disposable in items.Values.OfType()) - { - disposable.Dispose(); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace YamlDotNet.Serialization.Utilities +{ + /// + /// A generic container that is preserved during the entire deserialization process. + /// Any disposable object added to this collection will be disposed when this object is disposed. + /// + public sealed class SerializerState : IDisposable + { + private readonly IDictionary items = new Dictionary(); + + public T Get() + where T : class, new() + { + if (!items.TryGetValue(typeof(T), out var value)) + { + value = new T(); + items.Add(typeof(T), value); + } + return (T)value; + } + + /// + /// Invokes on all + /// objects added to this collection that implement . + /// + public void OnDeserialization() + { + foreach (var callback in items.Values.OfType()) + { + callback.OnDeserialization(); + } + } + + public void Dispose() + { + foreach (var disposable in items.Values.OfType()) + { + disposable.Dispose(); + } + } + } +} diff --git a/YamlDotNet/Serialization/Utilities/StringExtensions.cs b/YamlDotNet/Serialization/Utilities/StringExtensions.cs index d4c3cfdd5..7e57f1dc0 100644 --- a/YamlDotNet/Serialization/Utilities/StringExtensions.cs +++ b/YamlDotNet/Serialization/Utilities/StringExtensions.cs @@ -1,79 +1,79 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Text.RegularExpressions; - -namespace YamlDotNet.Serialization.Utilities -{ - /// - /// Various string extension methods - /// - internal static class StringExtensions - { - private static string ToCamelOrPascalCase(string str, Func firstLetterTransform) - { - var text = Regex.Replace(str, "([_\\-])(?[a-z])", match => match.Groups["char"].Value.ToUpperInvariant(), RegexOptions.IgnoreCase); - return firstLetterTransform(text[0]) + text.Substring(1); - } - - - /// - /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to - /// camel case (thisIsATest). Camel case is the same as Pascal case, except the first letter - /// is lowercase. - /// - /// String to convert - /// Converted string - public static string ToCamelCase(this string str) - { - return ToCamelOrPascalCase(str, char.ToLowerInvariant); - } - - /// - /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to - /// pascal case (ThisIsATest). Pascal case is the same as camel case, except the first letter - /// is uppercase. - /// - /// String to convert - /// Converted string - public static string ToPascalCase(this string str) - { - return ToCamelOrPascalCase(str, char.ToUpperInvariant); - } - - /// - /// Convert the string from camelcase (thisIsATest) to a hyphenated (this-is-a-test) or - /// underscored (this_is_a_test) string - /// - /// String to convert - /// Separator to use between segments - /// Converted string - public static string FromCamelCase(this string str, string separator) - { - // Ensure first letter is always lowercase - str = char.ToLower(str[0]) + str.Substring(1); - - str = Regex.Replace(str.ToCamelCase(), "(?[A-Z])", match => separator + match.Groups["char"].Value.ToLowerInvariant()); - return str; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Text.RegularExpressions; + +namespace YamlDotNet.Serialization.Utilities +{ + /// + /// Various string extension methods + /// + internal static class StringExtensions + { + private static string ToCamelOrPascalCase(string str, Func firstLetterTransform) + { + var text = Regex.Replace(str, "([_\\-])(?[a-z])", match => match.Groups["char"].Value.ToUpperInvariant(), RegexOptions.IgnoreCase); + return firstLetterTransform(text[0]) + text.Substring(1); + } + + + /// + /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to + /// camel case (thisIsATest). Camel case is the same as Pascal case, except the first letter + /// is lowercase. + /// + /// String to convert + /// Converted string + public static string ToCamelCase(this string str) + { + return ToCamelOrPascalCase(str, char.ToLowerInvariant); + } + + /// + /// Convert the string with underscores (this_is_a_test) or hyphens (this-is-a-test) to + /// pascal case (ThisIsATest). Pascal case is the same as camel case, except the first letter + /// is uppercase. + /// + /// String to convert + /// Converted string + public static string ToPascalCase(this string str) + { + return ToCamelOrPascalCase(str, char.ToUpperInvariant); + } + + /// + /// Convert the string from camelcase (thisIsATest) to a hyphenated (this-is-a-test) or + /// underscored (this_is_a_test) string + /// + /// String to convert + /// Separator to use between segments + /// Converted string + public static string FromCamelCase(this string str, string separator) + { + // Ensure first letter is always lowercase + str = char.ToLower(str[0]) + str.Substring(1); + + str = Regex.Replace(str.ToCamelCase(), "(?[A-Z])", match => separator + match.Groups["char"].Value.ToLowerInvariant()); + return str; + } + } +} diff --git a/YamlDotNet/Serialization/Utilities/TypeConverter.cs b/YamlDotNet/Serialization/Utilities/TypeConverter.cs index 39b155578..314bc81f4 100644 --- a/YamlDotNet/Serialization/Utilities/TypeConverter.cs +++ b/YamlDotNet/Serialization/Utilities/TypeConverter.cs @@ -1,268 +1,268 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// Remarks: This file is imported from the SixPack library. This is ok because -// the copyright holder has agreed to redistribute this file under the license -// used in YamlDotNet. - -using System; -using System.Globalization; -using System.Reflection; -using System.ComponentModel; - -namespace YamlDotNet.Serialization.Utilities -{ - /// - /// Performs type conversions using every standard provided by the .NET library. - /// - public static partial class TypeConverter - { - /// - /// Converts the specified value. - /// - /// The type to which the value is to be converted. - /// The value to convert. - /// - public static T ChangeType(object? value) - { - return (T)ChangeType(value, typeof(T))!; // This cast should always be valid - } - - /// - /// Converts the specified value. - /// - /// The type to which the value is to be converted. - /// The value to convert. - /// The provider. - /// - public static T ChangeType(object? value, IFormatProvider provider) - { - return (T)ChangeType(value, typeof(T), provider)!; // This cast should always be valid - } - - /// - /// Converts the specified value. - /// - /// The type to which the value is to be converted. - /// The value to convert. - /// The culture. - /// - public static T ChangeType(object? value, CultureInfo culture) - { - return (T)ChangeType(value, typeof(T), culture)!; // This cast should always be valid - } - - /// - /// Converts the specified value using the invariant culture. - /// - /// The value to convert. - /// The type to which the value is to be converted. - /// - public static object? ChangeType(object? value, Type destinationType) - { - return ChangeType(value, destinationType, CultureInfo.InvariantCulture); - } - - /// - /// Converts the specified value. - /// - /// The value to convert. - /// The type to which the value is to be converted. - /// The format provider. - /// - public static object? ChangeType(object? value, Type destinationType, IFormatProvider provider) - { - return ChangeType(value, destinationType, new CultureInfoAdapter(CultureInfo.CurrentCulture, provider)); - } - - /// - /// Converts the specified value. - /// - /// The value to convert. - /// The type to which the value is to be converted. - /// The culture. - /// - public static object? ChangeType(object? value, Type destinationType, CultureInfo culture) - { - // Handle null and DBNull - if (value == null || value.IsDbNull()) - { - return destinationType.IsValueType() ? Activator.CreateInstance(destinationType) : null; - } - - var sourceType = value.GetType(); - - // If the source type is compatible with the destination type, no conversion is needed - if (destinationType == sourceType || destinationType.IsAssignableFrom(sourceType)) - { - return value; - } - - // Nullable types get a special treatment - if (destinationType.IsGenericType()) - { - var genericTypeDefinition = destinationType.GetGenericTypeDefinition(); - if (genericTypeDefinition == typeof(Nullable<>)) - { - var innerType = destinationType.GetGenericArguments()[0]; - var convertedValue = ChangeType(value, innerType, culture); - return Activator.CreateInstance(destinationType, convertedValue); - } - } - - // Enums also require special handling - if (destinationType.IsEnum()) - { - return value is string valueText - ? Enum.Parse(destinationType, valueText, true) - : value; - } - - // Special case for booleans to support parsing "1" and "0". This is - // necessary for compatibility with XML Schema. - if (destinationType == typeof(bool)) - { - if ("0".Equals(value)) - { - return false; - } - - if ("1".Equals(value)) - { - return true; - } - } - - // Try with the source type's converter - var sourceConverter = TypeDescriptor.GetConverter(sourceType); - if (sourceConverter != null && sourceConverter.CanConvertTo(destinationType)) - { - return sourceConverter.ConvertTo(null, culture, value, destinationType); - } - - // Try with the destination type's converter - var destinationConverter = TypeDescriptor.GetConverter(destinationType); - if (destinationConverter != null && destinationConverter.CanConvertFrom(sourceType)) - { - return destinationConverter.ConvertFrom(null, culture, value); - } - - // Try to find a casting operator in the source or destination type - foreach (var type in new[] { sourceType, destinationType }) - { - foreach (var method in type.GetPublicStaticMethods()) - { - var isCastingOperator = - method.IsSpecialName && - (method.Name == "op_Implicit" || method.Name == "op_Explicit") && - destinationType.IsAssignableFrom(method.ReturnParameter.ParameterType); - - if (isCastingOperator) - { - var parameters = method.GetParameters(); - - var isCompatible = - parameters.Length == 1 && - parameters[0].ParameterType.IsAssignableFrom(sourceType); - - if (isCompatible) - { - try - { - return method.Invoke(null, new[] { value }); - } - catch (TargetInvocationException ex) - { - throw ex.Unwrap(); - } - } - } - } - } - - // If source type is string, try to find a Parse or TryParse method - if (sourceType == typeof(string)) - { - try - { - // Try with - public static T Parse(string, IFormatProvider) - var parseMethod = destinationType.GetPublicStaticMethod("Parse", typeof(string), typeof(IFormatProvider)); - if (parseMethod != null) - { - return parseMethod.Invoke(null, new object[] { value, culture }); - } - - // Try with - public static T Parse(string) - parseMethod = destinationType.GetPublicStaticMethod("Parse", typeof(string)); - if (parseMethod != null) - { - return parseMethod.Invoke(null, new object[] { value }); - } - } - catch (TargetInvocationException ex) - { - throw ex.Unwrap(); - } - } - - // Handle TimeSpan - if (destinationType == typeof(TimeSpan)) - { - return TimeSpan.Parse((string)ChangeType(value, typeof(string), CultureInfo.InvariantCulture)!); - } - - // Default to the Convert class - return Convert.ChangeType(value, destinationType, CultureInfo.InvariantCulture); - } - } -} - -#if !(NETSTANDARD1_3 || UNITY) -namespace YamlDotNet.Serialization.Utilities -{ - using System.Linq; - - partial class TypeConverter - { - /// - /// Registers a dynamically. - /// - /// The type to which the converter should be associated. - /// The type of the converter. -#if NET20 || NET35 || NET45 - [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")] -#endif - public static void RegisterTypeConverter() - where TConverter : System.ComponentModel.TypeConverter - { - var alreadyRegistered = TypeDescriptor.GetAttributes(typeof(TConvertible)) - .OfType() - .Any(a => a.ConverterTypeName == typeof(TConverter).AssemblyQualifiedName); - - if (!alreadyRegistered) - { - TypeDescriptor.AddAttributes(typeof(TConvertible), new TypeConverterAttribute(typeof(TConverter))); - } - } - - } -} -#endif +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Remarks: This file is imported from the SixPack library. This is ok because +// the copyright holder has agreed to redistribute this file under the license +// used in YamlDotNet. + +using System; +using System.Globalization; +using System.Reflection; +using System.ComponentModel; + +namespace YamlDotNet.Serialization.Utilities +{ + /// + /// Performs type conversions using every standard provided by the .NET library. + /// + public static partial class TypeConverter + { + /// + /// Converts the specified value. + /// + /// The type to which the value is to be converted. + /// The value to convert. + /// + public static T ChangeType(object? value) + { + return (T)ChangeType(value, typeof(T))!; // This cast should always be valid + } + + /// + /// Converts the specified value. + /// + /// The type to which the value is to be converted. + /// The value to convert. + /// The provider. + /// + public static T ChangeType(object? value, IFormatProvider provider) + { + return (T)ChangeType(value, typeof(T), provider)!; // This cast should always be valid + } + + /// + /// Converts the specified value. + /// + /// The type to which the value is to be converted. + /// The value to convert. + /// The culture. + /// + public static T ChangeType(object? value, CultureInfo culture) + { + return (T)ChangeType(value, typeof(T), culture)!; // This cast should always be valid + } + + /// + /// Converts the specified value using the invariant culture. + /// + /// The value to convert. + /// The type to which the value is to be converted. + /// + public static object? ChangeType(object? value, Type destinationType) + { + return ChangeType(value, destinationType, CultureInfo.InvariantCulture); + } + + /// + /// Converts the specified value. + /// + /// The value to convert. + /// The type to which the value is to be converted. + /// The format provider. + /// + public static object? ChangeType(object? value, Type destinationType, IFormatProvider provider) + { + return ChangeType(value, destinationType, new CultureInfoAdapter(CultureInfo.CurrentCulture, provider)); + } + + /// + /// Converts the specified value. + /// + /// The value to convert. + /// The type to which the value is to be converted. + /// The culture. + /// + public static object? ChangeType(object? value, Type destinationType, CultureInfo culture) + { + // Handle null and DBNull + if (value == null || value.IsDbNull()) + { + return destinationType.IsValueType() ? Activator.CreateInstance(destinationType) : null; + } + + var sourceType = value.GetType(); + + // If the source type is compatible with the destination type, no conversion is needed + if (destinationType == sourceType || destinationType.IsAssignableFrom(sourceType)) + { + return value; + } + + // Nullable types get a special treatment + if (destinationType.IsGenericType()) + { + var genericTypeDefinition = destinationType.GetGenericTypeDefinition(); + if (genericTypeDefinition == typeof(Nullable<>)) + { + var innerType = destinationType.GetGenericArguments()[0]; + var convertedValue = ChangeType(value, innerType, culture); + return Activator.CreateInstance(destinationType, convertedValue); + } + } + + // Enums also require special handling + if (destinationType.IsEnum()) + { + return value is string valueText + ? Enum.Parse(destinationType, valueText, true) + : value; + } + + // Special case for booleans to support parsing "1" and "0". This is + // necessary for compatibility with XML Schema. + if (destinationType == typeof(bool)) + { + if ("0".Equals(value)) + { + return false; + } + + if ("1".Equals(value)) + { + return true; + } + } + + // Try with the source type's converter + var sourceConverter = TypeDescriptor.GetConverter(sourceType); + if (sourceConverter != null && sourceConverter.CanConvertTo(destinationType)) + { + return sourceConverter.ConvertTo(null, culture, value, destinationType); + } + + // Try with the destination type's converter + var destinationConverter = TypeDescriptor.GetConverter(destinationType); + if (destinationConverter != null && destinationConverter.CanConvertFrom(sourceType)) + { + return destinationConverter.ConvertFrom(null, culture, value); + } + + // Try to find a casting operator in the source or destination type + foreach (var type in new[] { sourceType, destinationType }) + { + foreach (var method in type.GetPublicStaticMethods()) + { + var isCastingOperator = + method.IsSpecialName && + (method.Name == "op_Implicit" || method.Name == "op_Explicit") && + destinationType.IsAssignableFrom(method.ReturnParameter.ParameterType); + + if (isCastingOperator) + { + var parameters = method.GetParameters(); + + var isCompatible = + parameters.Length == 1 && + parameters[0].ParameterType.IsAssignableFrom(sourceType); + + if (isCompatible) + { + try + { + return method.Invoke(null, new[] { value }); + } + catch (TargetInvocationException ex) + { + throw ex.Unwrap(); + } + } + } + } + } + + // If source type is string, try to find a Parse or TryParse method + if (sourceType == typeof(string)) + { + try + { + // Try with - public static T Parse(string, IFormatProvider) + var parseMethod = destinationType.GetPublicStaticMethod("Parse", typeof(string), typeof(IFormatProvider)); + if (parseMethod != null) + { + return parseMethod.Invoke(null, new object[] { value, culture }); + } + + // Try with - public static T Parse(string) + parseMethod = destinationType.GetPublicStaticMethod("Parse", typeof(string)); + if (parseMethod != null) + { + return parseMethod.Invoke(null, new object[] { value }); + } + } + catch (TargetInvocationException ex) + { + throw ex.Unwrap(); + } + } + + // Handle TimeSpan + if (destinationType == typeof(TimeSpan)) + { + return TimeSpan.Parse((string)ChangeType(value, typeof(string), CultureInfo.InvariantCulture)!); + } + + // Default to the Convert class + return Convert.ChangeType(value, destinationType, CultureInfo.InvariantCulture); + } + } +} + +#if !(NETSTANDARD1_3 || UNITY) +namespace YamlDotNet.Serialization.Utilities +{ + using System.Linq; + + partial class TypeConverter + { + /// + /// Registers a dynamically. + /// + /// The type to which the converter should be associated. + /// The type of the converter. +#if NET20 || NET35 || NET45 + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")] +#endif + public static void RegisterTypeConverter() + where TConverter : System.ComponentModel.TypeConverter + { + var alreadyRegistered = TypeDescriptor.GetAttributes(typeof(TConvertible)) + .OfType() + .Any(a => a.ConverterTypeName == typeof(TConverter).AssemblyQualifiedName); + + if (!alreadyRegistered) + { + TypeDescriptor.AddAttributes(typeof(TConvertible), new TypeConverterAttribute(typeof(TConverter))); + } + } + + } +} +#endif diff --git a/YamlDotNet/Serialization/ValueDeserializers/AliasValueDeserializer.cs b/YamlDotNet/Serialization/ValueDeserializers/AliasValueDeserializer.cs index 3a50d129a..8c82f3ad0 100644 --- a/YamlDotNet/Serialization/ValueDeserializers/AliasValueDeserializer.cs +++ b/YamlDotNet/Serialization/ValueDeserializers/AliasValueDeserializer.cs @@ -1,147 +1,147 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.ValueDeserializers -{ - public sealed class AliasValueDeserializer : IValueDeserializer - { - private readonly IValueDeserializer innerDeserializer; - - public AliasValueDeserializer(IValueDeserializer innerDeserializer) - { - this.innerDeserializer = innerDeserializer ?? throw new ArgumentNullException(nameof(innerDeserializer)); - } - - private sealed class AliasState : Dictionary, IPostDeserializationCallback - { - public void OnDeserialization() - { - foreach (var promise in Values) - { - if (!promise.HasValue) - { - var alias = promise.Alias!; // When the value is not known, the alias is always known - throw new AnchorNotFoundException(alias.Start, alias.End, $"Anchor '{alias.Value}' not found"); - } - } - } - } - - private sealed class ValuePromise : IValuePromise - { - public event Action? ValueAvailable; - - public bool HasValue { get; private set; } - - private object? value; - - public readonly AnchorAlias? Alias; - - public ValuePromise(AnchorAlias alias) - { - this.Alias = alias; - } - - public ValuePromise(object? value) - { - HasValue = true; - this.value = value; - } - - public object? Value - { - get - { - if (!HasValue) - { - throw new InvalidOperationException("Value not set"); - } - return value; - } - set - { - if (HasValue) - { - throw new InvalidOperationException("Value already set"); - } - HasValue = true; - this.value = value; - - ValueAvailable?.Invoke(value); - } - } - } - - public object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) - { - object? value; - if (parser.TryConsume(out var alias)) - { - var aliasState = state.Get(); - if (!aliasState.TryGetValue(alias.Value, out var valuePromise)) - { - throw new AnchorNotFoundException(alias.Start, alias.End, $"Alias ${alias.Value} cannot precede anchor declaration"); - } - - return valuePromise.HasValue ? valuePromise.Value : valuePromise; - } - - var anchor = AnchorName.Empty; - if (parser.Accept(out var nodeEvent) && !nodeEvent.Anchor.IsEmpty) - { - anchor = nodeEvent.Anchor; - var aliasState = state.Get(); - if (!aliasState.ContainsKey(anchor)) - { - aliasState[anchor] = new ValuePromise(new AnchorAlias(anchor)); - } - } - - value = innerDeserializer.DeserializeValue(parser, expectedType, state, nestedObjectDeserializer); - - if (!anchor.IsEmpty) - { - var aliasState = state.Get(); - - if (!aliasState.TryGetValue(anchor, out var valuePromise)) - { - aliasState.Add(anchor, new ValuePromise(value)); - } - else if (!valuePromise.HasValue) - { - valuePromise.Value = value; - } - else - { - aliasState[anchor] = new ValuePromise(value); - } - } - - return value; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.ValueDeserializers +{ + public sealed class AliasValueDeserializer : IValueDeserializer + { + private readonly IValueDeserializer innerDeserializer; + + public AliasValueDeserializer(IValueDeserializer innerDeserializer) + { + this.innerDeserializer = innerDeserializer ?? throw new ArgumentNullException(nameof(innerDeserializer)); + } + + private sealed class AliasState : Dictionary, IPostDeserializationCallback + { + public void OnDeserialization() + { + foreach (var promise in Values) + { + if (!promise.HasValue) + { + var alias = promise.Alias!; // When the value is not known, the alias is always known + throw new AnchorNotFoundException(alias.Start, alias.End, $"Anchor '{alias.Value}' not found"); + } + } + } + } + + private sealed class ValuePromise : IValuePromise + { + public event Action? ValueAvailable; + + public bool HasValue { get; private set; } + + private object? value; + + public readonly AnchorAlias? Alias; + + public ValuePromise(AnchorAlias alias) + { + this.Alias = alias; + } + + public ValuePromise(object? value) + { + HasValue = true; + this.value = value; + } + + public object? Value + { + get + { + if (!HasValue) + { + throw new InvalidOperationException("Value not set"); + } + return value; + } + set + { + if (HasValue) + { + throw new InvalidOperationException("Value already set"); + } + HasValue = true; + this.value = value; + + ValueAvailable?.Invoke(value); + } + } + } + + public object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) + { + object? value; + if (parser.TryConsume(out var alias)) + { + var aliasState = state.Get(); + if (!aliasState.TryGetValue(alias.Value, out var valuePromise)) + { + throw new AnchorNotFoundException(alias.Start, alias.End, $"Alias ${alias.Value} cannot precede anchor declaration"); + } + + return valuePromise.HasValue ? valuePromise.Value : valuePromise; + } + + var anchor = AnchorName.Empty; + if (parser.Accept(out var nodeEvent) && !nodeEvent.Anchor.IsEmpty) + { + anchor = nodeEvent.Anchor; + var aliasState = state.Get(); + if (!aliasState.ContainsKey(anchor)) + { + aliasState[anchor] = new ValuePromise(new AnchorAlias(anchor)); + } + } + + value = innerDeserializer.DeserializeValue(parser, expectedType, state, nestedObjectDeserializer); + + if (!anchor.IsEmpty) + { + var aliasState = state.Get(); + + if (!aliasState.TryGetValue(anchor, out var valuePromise)) + { + aliasState.Add(anchor, new ValuePromise(value)); + } + else if (!valuePromise.HasValue) + { + valuePromise.Value = value; + } + else + { + aliasState[anchor] = new ValuePromise(value); + } + } + + return value; + } + } +} diff --git a/YamlDotNet/Serialization/ValueDeserializers/NodeValueDeserializer.cs b/YamlDotNet/Serialization/ValueDeserializers/NodeValueDeserializer.cs index 5bb9b8d59..da17e2d46 100644 --- a/YamlDotNet/Serialization/ValueDeserializers/NodeValueDeserializer.cs +++ b/YamlDotNet/Serialization/ValueDeserializers/NodeValueDeserializer.cs @@ -1,89 +1,89 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization.Utilities; - -namespace YamlDotNet.Serialization.ValueDeserializers -{ - public sealed class NodeValueDeserializer : IValueDeserializer - { - private readonly IList deserializers; - private readonly IList typeResolvers; - - public NodeValueDeserializer(IList deserializers, IList typeResolvers) - { - this.deserializers = deserializers ?? throw new ArgumentNullException(nameof(deserializers)); - this.typeResolvers = typeResolvers ?? throw new ArgumentNullException(nameof(typeResolvers)); - } - - public object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) - { - parser.Accept(out var nodeEvent); - var nodeType = GetTypeFromEvent(nodeEvent, expectedType); - - try - { - foreach (var deserializer in deserializers) - { - if (deserializer.Deserialize(parser, nodeType, (r, t) => nestedObjectDeserializer.DeserializeValue(r, t, state, nestedObjectDeserializer), out var value)) - { - return TypeConverter.ChangeType(value, expectedType); - } - } - } - catch (YamlException) - { - throw; - } - catch (Exception ex) - { - throw new YamlException( - nodeEvent?.Start ?? Mark.Empty, - nodeEvent?.End ?? Mark.Empty, - "Exception during deserialization", - ex - ); - } - - throw new YamlException( - nodeEvent?.Start ?? Mark.Empty, - nodeEvent?.End ?? Mark.Empty, - $"No node deserializer was able to deserialize the node into type {expectedType.AssemblyQualifiedName}" - ); - } - - private Type GetTypeFromEvent(NodeEvent? nodeEvent, Type currentType) - { - foreach (var typeResolver in typeResolvers) - { - if (typeResolver.Resolve(nodeEvent, ref currentType)) - { - break; - } - } - return currentType; - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization.Utilities; + +namespace YamlDotNet.Serialization.ValueDeserializers +{ + public sealed class NodeValueDeserializer : IValueDeserializer + { + private readonly IList deserializers; + private readonly IList typeResolvers; + + public NodeValueDeserializer(IList deserializers, IList typeResolvers) + { + this.deserializers = deserializers ?? throw new ArgumentNullException(nameof(deserializers)); + this.typeResolvers = typeResolvers ?? throw new ArgumentNullException(nameof(typeResolvers)); + } + + public object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) + { + parser.Accept(out var nodeEvent); + var nodeType = GetTypeFromEvent(nodeEvent, expectedType); + + try + { + foreach (var deserializer in deserializers) + { + if (deserializer.Deserialize(parser, nodeType, (r, t) => nestedObjectDeserializer.DeserializeValue(r, t, state, nestedObjectDeserializer), out var value)) + { + return TypeConverter.ChangeType(value, expectedType); + } + } + } + catch (YamlException) + { + throw; + } + catch (Exception ex) + { + throw new YamlException( + nodeEvent?.Start ?? Mark.Empty, + nodeEvent?.End ?? Mark.Empty, + "Exception during deserialization", + ex + ); + } + + throw new YamlException( + nodeEvent?.Start ?? Mark.Empty, + nodeEvent?.End ?? Mark.Empty, + $"No node deserializer was able to deserialize the node into type {expectedType.AssemblyQualifiedName}" + ); + } + + private Type GetTypeFromEvent(NodeEvent? nodeEvent, Type currentType) + { + foreach (var typeResolver in typeResolvers) + { + if (typeResolver.Resolve(nodeEvent, ref currentType)) + { + break; + } + } + return currentType; + } + } +} diff --git a/YamlDotNet/Serialization/YamlAttributeOverrides.cs b/YamlDotNet/Serialization/YamlAttributeOverrides.cs index 456e01ca0..efd82a15c 100644 --- a/YamlDotNet/Serialization/YamlAttributeOverrides.cs +++ b/YamlDotNet/Serialization/YamlAttributeOverrides.cs @@ -1,198 +1,198 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using HashCode = YamlDotNet.Core.HashCode; - -namespace YamlDotNet.Serialization -{ - /// - /// Define a collection of YamlAttribute Overrides for pre-defined object types. - /// - public sealed partial class YamlAttributeOverrides - { - private struct AttributeKey - { - public readonly Type AttributeType; - public readonly string PropertyName; - - public AttributeKey(Type attributeType, string propertyName) - { - AttributeType = attributeType; - PropertyName = propertyName; - } - - public override bool Equals(object? obj) - { - return obj is AttributeKey other - && AttributeType.Equals(other.AttributeType) - && PropertyName.Equals(other.PropertyName); - } - - public override int GetHashCode() - { - return HashCode.CombineHashCodes(AttributeType.GetHashCode(), PropertyName.GetHashCode()); - } - } - - private sealed class AttributeMapping - { - public readonly Type RegisteredType; - public readonly Attribute Attribute; - - public AttributeMapping(Type registeredType, Attribute attribute) - { - this.RegisteredType = registeredType; - Attribute = attribute; - } - - public override bool Equals(object? obj) - { - return obj is AttributeMapping other - && RegisteredType.Equals(other.RegisteredType) - && Attribute.Equals(other.Attribute); - } - - public override int GetHashCode() - { - return HashCode.CombineHashCodes(RegisteredType.GetHashCode(), Attribute.GetHashCode()); - } - - /// - /// Checks whether this mapping matches the specified type, and returns a value indicating the match priority. - /// - /// The priority of the match. Higher values have more priority. Zero indicates no match. - public int Matches(Type matchType) - { - var currentPriority = 0; - var currentType = matchType; - while (currentType != null) - { - ++currentPriority; - if (currentType == RegisteredType) - { - return currentPriority; - } - currentType = currentType.BaseType(); - } - - if (matchType.GetInterfaces().Contains(RegisteredType)) - { - return currentPriority; - } - - return 0; - } - } - - private readonly Dictionary> overrides = new Dictionary>(); - - [return: MaybeNull] - public T GetAttribute(Type type, string member) where T : Attribute - { - if (overrides.TryGetValue(new AttributeKey(typeof(T), member), out var mappings)) - { - var bestMatchPriority = 0; - AttributeMapping? bestMatch = null; - - foreach (var mapping in mappings) - { - var priority = mapping.Matches(type); - if (priority > bestMatchPriority) - { - bestMatchPriority = priority; - bestMatch = mapping; - } - } - - if (bestMatchPriority > 0) - { - return (T)bestMatch!.Attribute; - } - } - - return default; - } - - /// - /// Adds a Member Attribute Override - /// - /// Type - /// Class Member - /// Overriding Attribute - public void Add(Type type, string member, Attribute attribute) - { - var mapping = new AttributeMapping(type, attribute); - - var attributeKey = new AttributeKey(attribute.GetType(), member); - if (!overrides.TryGetValue(attributeKey, out var mappings)) - { - mappings = new List(); - overrides.Add(attributeKey, mappings); - } - else if (mappings.Contains(mapping)) - { - throw new InvalidOperationException($"Attribute ({attribute}) already set for Type {type.FullName}, Member {member}"); - } - - mappings.Add(mapping); - } - - /// - /// Creates a copy of this instance. - /// - public YamlAttributeOverrides Clone() - { - var clone = new YamlAttributeOverrides(); - foreach (var entry in overrides) - { - foreach (var item in entry.Value) - { - clone.Add(item.RegisteredType, entry.Key.PropertyName, item.Attribute); - } - } - return clone; - } - } -} - -#if !NET20 -namespace YamlDotNet.Serialization -{ - using System.Linq.Expressions; - using YamlDotNet.Helpers; - - partial class YamlAttributeOverrides - { - /// - /// Adds a Member Attribute Override - /// - public void Add(Expression> propertyAccessor, Attribute attribute) - { - var property = propertyAccessor.AsProperty(); - Add(typeof(TClass), property.Name, attribute); - } - } -} -#endif +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using HashCode = YamlDotNet.Core.HashCode; + +namespace YamlDotNet.Serialization +{ + /// + /// Define a collection of YamlAttribute Overrides for pre-defined object types. + /// + public sealed partial class YamlAttributeOverrides + { + private struct AttributeKey + { + public readonly Type AttributeType; + public readonly string PropertyName; + + public AttributeKey(Type attributeType, string propertyName) + { + AttributeType = attributeType; + PropertyName = propertyName; + } + + public override bool Equals(object? obj) + { + return obj is AttributeKey other + && AttributeType.Equals(other.AttributeType) + && PropertyName.Equals(other.PropertyName); + } + + public override int GetHashCode() + { + return HashCode.CombineHashCodes(AttributeType.GetHashCode(), PropertyName.GetHashCode()); + } + } + + private sealed class AttributeMapping + { + public readonly Type RegisteredType; + public readonly Attribute Attribute; + + public AttributeMapping(Type registeredType, Attribute attribute) + { + this.RegisteredType = registeredType; + Attribute = attribute; + } + + public override bool Equals(object? obj) + { + return obj is AttributeMapping other + && RegisteredType.Equals(other.RegisteredType) + && Attribute.Equals(other.Attribute); + } + + public override int GetHashCode() + { + return HashCode.CombineHashCodes(RegisteredType.GetHashCode(), Attribute.GetHashCode()); + } + + /// + /// Checks whether this mapping matches the specified type, and returns a value indicating the match priority. + /// + /// The priority of the match. Higher values have more priority. Zero indicates no match. + public int Matches(Type matchType) + { + var currentPriority = 0; + var currentType = matchType; + while (currentType != null) + { + ++currentPriority; + if (currentType == RegisteredType) + { + return currentPriority; + } + currentType = currentType.BaseType(); + } + + if (matchType.GetInterfaces().Contains(RegisteredType)) + { + return currentPriority; + } + + return 0; + } + } + + private readonly Dictionary> overrides = new Dictionary>(); + + [return: MaybeNull] + public T GetAttribute(Type type, string member) where T : Attribute + { + if (overrides.TryGetValue(new AttributeKey(typeof(T), member), out var mappings)) + { + var bestMatchPriority = 0; + AttributeMapping? bestMatch = null; + + foreach (var mapping in mappings) + { + var priority = mapping.Matches(type); + if (priority > bestMatchPriority) + { + bestMatchPriority = priority; + bestMatch = mapping; + } + } + + if (bestMatchPriority > 0) + { + return (T)bestMatch!.Attribute; + } + } + + return default; + } + + /// + /// Adds a Member Attribute Override + /// + /// Type + /// Class Member + /// Overriding Attribute + public void Add(Type type, string member, Attribute attribute) + { + var mapping = new AttributeMapping(type, attribute); + + var attributeKey = new AttributeKey(attribute.GetType(), member); + if (!overrides.TryGetValue(attributeKey, out var mappings)) + { + mappings = new List(); + overrides.Add(attributeKey, mappings); + } + else if (mappings.Contains(mapping)) + { + throw new InvalidOperationException($"Attribute ({attribute}) already set for Type {type.FullName}, Member {member}"); + } + + mappings.Add(mapping); + } + + /// + /// Creates a copy of this instance. + /// + public YamlAttributeOverrides Clone() + { + var clone = new YamlAttributeOverrides(); + foreach (var entry in overrides) + { + foreach (var item in entry.Value) + { + clone.Add(item.RegisteredType, entry.Key.PropertyName, item.Attribute); + } + } + return clone; + } + } +} + +#if !NET20 +namespace YamlDotNet.Serialization +{ + using System.Linq.Expressions; + using YamlDotNet.Helpers; + + partial class YamlAttributeOverrides + { + /// + /// Adds a Member Attribute Override + /// + public void Add(Expression> propertyAccessor, Attribute attribute) + { + var property = propertyAccessor.AsProperty(); + Add(typeof(TClass), property.Name, attribute); + } + } +} +#endif diff --git a/YamlDotNet/Serialization/YamlAttributeOverridesInspector.cs b/YamlDotNet/Serialization/YamlAttributeOverridesInspector.cs index b8dbe3531..1d88d7a91 100644 --- a/YamlDotNet/Serialization/YamlAttributeOverridesInspector.cs +++ b/YamlDotNet/Serialization/YamlAttributeOverridesInspector.cs @@ -1,110 +1,110 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using YamlDotNet.Core; -using YamlDotNet.Serialization.TypeInspectors; - -namespace YamlDotNet.Serialization -{ - /// - /// Applies the Yaml attribute overrides to another . - /// - public sealed class YamlAttributeOverridesInspector : TypeInspectorSkeleton - { - private readonly ITypeInspector innerTypeDescriptor; - private readonly YamlAttributeOverrides overrides; - - public YamlAttributeOverridesInspector(ITypeInspector innerTypeDescriptor, YamlAttributeOverrides overrides) - { - this.innerTypeDescriptor = innerTypeDescriptor; - this.overrides = overrides; - } - - public override IEnumerable GetProperties(Type type, object? container) - { - var properties = innerTypeDescriptor.GetProperties(type, container); - if (overrides != null) - { - properties = properties - .Select(p => (IPropertyDescriptor)new OverridePropertyDescriptor(p, overrides, type)); - } - - return properties; - } - - public sealed class OverridePropertyDescriptor : IPropertyDescriptor - { - private readonly IPropertyDescriptor baseDescriptor; - private readonly YamlAttributeOverrides overrides; - private readonly Type classType; - - public OverridePropertyDescriptor(IPropertyDescriptor baseDescriptor, YamlAttributeOverrides overrides, Type classType) - { - this.baseDescriptor = baseDescriptor; - this.overrides = overrides; - this.classType = classType; - } - - public string Name { get { return baseDescriptor.Name; } } - - public bool CanWrite { get { return baseDescriptor.CanWrite; } } - - public Type Type { get { return baseDescriptor.Type; } } - - public Type? TypeOverride - { - get { return baseDescriptor.TypeOverride; } - set { baseDescriptor.TypeOverride = value; } - } - - public int Order - { - get { return baseDescriptor.Order; } - set { baseDescriptor.Order = value; } - } - - public ScalarStyle ScalarStyle - { - get { return baseDescriptor.ScalarStyle; } - set { baseDescriptor.ScalarStyle = value; } - } - - public void Write(object target, object? value) - { - baseDescriptor.Write(target, value); - } - - public T GetCustomAttribute() where T : Attribute - { - var attr = overrides.GetAttribute(classType, Name); - return attr ?? baseDescriptor.GetCustomAttribute(); - } - - public IObjectDescriptor Read(object target) - { - return baseDescriptor.Read(target); - } - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using YamlDotNet.Core; +using YamlDotNet.Serialization.TypeInspectors; + +namespace YamlDotNet.Serialization +{ + /// + /// Applies the Yaml attribute overrides to another . + /// + public sealed class YamlAttributeOverridesInspector : TypeInspectorSkeleton + { + private readonly ITypeInspector innerTypeDescriptor; + private readonly YamlAttributeOverrides overrides; + + public YamlAttributeOverridesInspector(ITypeInspector innerTypeDescriptor, YamlAttributeOverrides overrides) + { + this.innerTypeDescriptor = innerTypeDescriptor; + this.overrides = overrides; + } + + public override IEnumerable GetProperties(Type type, object? container) + { + var properties = innerTypeDescriptor.GetProperties(type, container); + if (overrides != null) + { + properties = properties + .Select(p => (IPropertyDescriptor)new OverridePropertyDescriptor(p, overrides, type)); + } + + return properties; + } + + public sealed class OverridePropertyDescriptor : IPropertyDescriptor + { + private readonly IPropertyDescriptor baseDescriptor; + private readonly YamlAttributeOverrides overrides; + private readonly Type classType; + + public OverridePropertyDescriptor(IPropertyDescriptor baseDescriptor, YamlAttributeOverrides overrides, Type classType) + { + this.baseDescriptor = baseDescriptor; + this.overrides = overrides; + this.classType = classType; + } + + public string Name { get { return baseDescriptor.Name; } } + + public bool CanWrite { get { return baseDescriptor.CanWrite; } } + + public Type Type { get { return baseDescriptor.Type; } } + + public Type? TypeOverride + { + get { return baseDescriptor.TypeOverride; } + set { baseDescriptor.TypeOverride = value; } + } + + public int Order + { + get { return baseDescriptor.Order; } + set { baseDescriptor.Order = value; } + } + + public ScalarStyle ScalarStyle + { + get { return baseDescriptor.ScalarStyle; } + set { baseDescriptor.ScalarStyle = value; } + } + + public void Write(object target, object? value) + { + baseDescriptor.Write(target, value); + } + + public T GetCustomAttribute() where T : Attribute + { + var attr = overrides.GetAttribute(classType, Name); + return attr ?? baseDescriptor.GetCustomAttribute(); + } + + public IObjectDescriptor Read(object target) + { + return baseDescriptor.Read(target); + } + } + } +} diff --git a/YamlDotNet/Serialization/YamlAttributesTypeInspector.cs b/YamlDotNet/Serialization/YamlAttributesTypeInspector.cs index 6f5903a3f..e8fe65fbc 100644 --- a/YamlDotNet/Serialization/YamlAttributesTypeInspector.cs +++ b/YamlDotNet/Serialization/YamlAttributesTypeInspector.cs @@ -1,70 +1,70 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using YamlDotNet.Serialization.TypeInspectors; - -namespace YamlDotNet.Serialization -{ - /// - /// Applies the Yaml* attributes to another . - /// - public sealed class YamlAttributesTypeInspector : TypeInspectorSkeleton - { - private readonly ITypeInspector innerTypeDescriptor; - - public YamlAttributesTypeInspector(ITypeInspector innerTypeDescriptor) - { - this.innerTypeDescriptor = innerTypeDescriptor; - } - - public override IEnumerable GetProperties(Type type, object? container) - { - return innerTypeDescriptor.GetProperties(type, container) - .Where(p => p.GetCustomAttribute() == null) - .Select(p => - { - var descriptor = new PropertyDescriptor(p); - var member = p.GetCustomAttribute(); - if (member != null) - { - if (member.SerializeAs != null) - { - descriptor.TypeOverride = member.SerializeAs; - } - - descriptor.Order = member.Order; - descriptor.ScalarStyle = member.ScalarStyle; - - if (member.Alias != null) - { - descriptor.Name = member.Alias; - } - } - - return (IPropertyDescriptor)descriptor; - }) - .OrderBy(p => p.Order); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using YamlDotNet.Serialization.TypeInspectors; + +namespace YamlDotNet.Serialization +{ + /// + /// Applies the Yaml* attributes to another . + /// + public sealed class YamlAttributesTypeInspector : TypeInspectorSkeleton + { + private readonly ITypeInspector innerTypeDescriptor; + + public YamlAttributesTypeInspector(ITypeInspector innerTypeDescriptor) + { + this.innerTypeDescriptor = innerTypeDescriptor; + } + + public override IEnumerable GetProperties(Type type, object? container) + { + return innerTypeDescriptor.GetProperties(type, container) + .Where(p => p.GetCustomAttribute() == null) + .Select(p => + { + var descriptor = new PropertyDescriptor(p); + var member = p.GetCustomAttribute(); + if (member != null) + { + if (member.SerializeAs != null) + { + descriptor.TypeOverride = member.SerializeAs; + } + + descriptor.Order = member.Order; + descriptor.ScalarStyle = member.ScalarStyle; + + if (member.Alias != null) + { + descriptor.Name = member.Alias; + } + } + + return (IPropertyDescriptor)descriptor; + }) + .OrderBy(p => p.Order); + } + } +} diff --git a/YamlDotNet/Serialization/YamlFormatter.cs b/YamlDotNet/Serialization/YamlFormatter.cs index 2b8e7b229..a91421db5 100644 --- a/YamlDotNet/Serialization/YamlFormatter.cs +++ b/YamlDotNet/Serialization/YamlFormatter.cs @@ -1,75 +1,75 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Globalization; - -namespace YamlDotNet.Serialization -{ - internal static class YamlFormatter - { - public static readonly NumberFormatInfo NumberFormat = new NumberFormatInfo - { - CurrencyDecimalSeparator = ".", - CurrencyGroupSeparator = "_", - CurrencyGroupSizes = new[] { 3 }, - CurrencySymbol = string.Empty, - CurrencyDecimalDigits = 99, - NumberDecimalSeparator = ".", - NumberGroupSeparator = "_", - NumberGroupSizes = new[] { 3 }, - NumberDecimalDigits = 99, - NaNSymbol = ".nan", - PositiveInfinitySymbol = ".inf", - NegativeInfinitySymbol = "-.inf" - }; - - public static string FormatNumber(object number) - { - return Convert.ToString(number, NumberFormat)!; - } - - public static string FormatNumber(double number) - { - return number.ToString("G", NumberFormat); - } - - public static string FormatNumber(float number) - { - return number.ToString("G", NumberFormat); - } - - public static string FormatBoolean(object boolean) - { - return boolean.Equals(true) ? "true" : "false"; - } - - public static string FormatDateTime(object dateTime) - { - return ((DateTime)dateTime).ToString("o", CultureInfo.InvariantCulture); - } - - public static string FormatTimeSpan(object timeSpan) - { - return ((TimeSpan)timeSpan).ToString(); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using System.Globalization; + +namespace YamlDotNet.Serialization +{ + internal static class YamlFormatter + { + public static readonly NumberFormatInfo NumberFormat = new NumberFormatInfo + { + CurrencyDecimalSeparator = ".", + CurrencyGroupSeparator = "_", + CurrencyGroupSizes = new[] { 3 }, + CurrencySymbol = string.Empty, + CurrencyDecimalDigits = 99, + NumberDecimalSeparator = ".", + NumberGroupSeparator = "_", + NumberGroupSizes = new[] { 3 }, + NumberDecimalDigits = 99, + NaNSymbol = ".nan", + PositiveInfinitySymbol = ".inf", + NegativeInfinitySymbol = "-.inf" + }; + + public static string FormatNumber(object number) + { + return Convert.ToString(number, NumberFormat)!; + } + + public static string FormatNumber(double number) + { + return number.ToString("G", NumberFormat); + } + + public static string FormatNumber(float number) + { + return number.ToString("G", NumberFormat); + } + + public static string FormatBoolean(object boolean) + { + return boolean.Equals(true) ? "true" : "false"; + } + + public static string FormatDateTime(object dateTime) + { + return ((DateTime)dateTime).ToString("o", CultureInfo.InvariantCulture); + } + + public static string FormatTimeSpan(object timeSpan) + { + return ((TimeSpan)timeSpan).ToString(); + } + } +} diff --git a/YamlDotNet/Serialization/YamlIgnoreAttribute.cs b/YamlDotNet/Serialization/YamlIgnoreAttribute.cs index f5a7d463b..1d812c911 100644 --- a/YamlDotNet/Serialization/YamlIgnoreAttribute.cs +++ b/YamlDotNet/Serialization/YamlIgnoreAttribute.cs @@ -1,34 +1,34 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace YamlDotNet.Serialization -{ - /// - /// Instructs the YamlSerializer not to serialize the public field or public read/write property value. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] - public sealed class YamlIgnoreAttribute : Attribute - { - } -} - +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; + +namespace YamlDotNet.Serialization +{ + /// + /// Instructs the YamlSerializer not to serialize the public field or public read/write property value. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public sealed class YamlIgnoreAttribute : Attribute + { + } +} + diff --git a/YamlDotNet/Serialization/YamlMemberAttribute.cs b/YamlDotNet/Serialization/YamlMemberAttribute.cs index ceafaefe3..42b6db93e 100644 --- a/YamlDotNet/Serialization/YamlMemberAttribute.cs +++ b/YamlDotNet/Serialization/YamlMemberAttribute.cs @@ -1,95 +1,95 @@ -// This file is part of YamlDotNet - A .NET library for YAML. -// Copyright (c) Antoine Aubry and contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using YamlDotNet.Core; - -namespace YamlDotNet.Serialization -{ - /// - /// Provides special Yaml serialization instructions. - /// - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] - public sealed class YamlMemberAttribute : Attribute - { - /// - /// Decription/Comment about this property. - /// When set, a comment will be emitted when serializing this member. - /// - public string? Description { get; set; } - - /// - /// Specifies that this property should be serialized as the given type, rather than using the actual runtime value's type. - /// - public Type? SerializeAs { get; set; } - - /// - /// Specifies the order priority of this property. - /// - public int Order { get; set; } - - /// - /// Instructs the to use a different field name for serialization. - /// - public string? Alias { get; set; } - - /// - /// When false, naming conventions will not be applied to this member. Defaults to true. - /// - public bool ApplyNamingConventions { get; set; } - - /// - /// Specifies the scalar style of the property when serialized. This will only affect the serialization of scalar properties. - /// - public ScalarStyle ScalarStyle { get; set; } - - private DefaultValuesHandling? defaultValuesHandling; - - /// - /// Overrides how null and default values should be handled for this property. - /// - public DefaultValuesHandling DefaultValuesHandling - { - get => defaultValuesHandling.GetValueOrDefault(); - set => defaultValuesHandling = value; - } - - public bool IsDefaultValuesHandlingSpecified => defaultValuesHandling.HasValue; - - /// - /// Initializes a new instance of the class. - /// - public YamlMemberAttribute() - { - ScalarStyle = ScalarStyle.Any; - ApplyNamingConventions = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// Specifies that this property should be serialized as the given type, rather than using the actual runtime value's type. - public YamlMemberAttribute(Type serializeAs) : this() - { - SerializeAs = serializeAs ?? throw new ArgumentNullException(nameof(serializeAs)); - } - } -} +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; + +namespace YamlDotNet.Serialization +{ + /// + /// Provides special Yaml serialization instructions. + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] + public sealed class YamlMemberAttribute : Attribute + { + /// + /// Decription/Comment about this property. + /// When set, a comment will be emitted when serializing this member. + /// + public string? Description { get; set; } + + /// + /// Specifies that this property should be serialized as the given type, rather than using the actual runtime value's type. + /// + public Type? SerializeAs { get; set; } + + /// + /// Specifies the order priority of this property. + /// + public int Order { get; set; } + + /// + /// Instructs the to use a different field name for serialization. + /// + public string? Alias { get; set; } + + /// + /// When false, naming conventions will not be applied to this member. Defaults to true. + /// + public bool ApplyNamingConventions { get; set; } + + /// + /// Specifies the scalar style of the property when serialized. This will only affect the serialization of scalar properties. + /// + public ScalarStyle ScalarStyle { get; set; } + + private DefaultValuesHandling? defaultValuesHandling; + + /// + /// Overrides how null and default values should be handled for this property. + /// + public DefaultValuesHandling DefaultValuesHandling + { + get => defaultValuesHandling.GetValueOrDefault(); + set => defaultValuesHandling = value; + } + + public bool IsDefaultValuesHandlingSpecified => defaultValuesHandling.HasValue; + + /// + /// Initializes a new instance of the class. + /// + public YamlMemberAttribute() + { + ScalarStyle = ScalarStyle.Any; + ApplyNamingConventions = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// Specifies that this property should be serialized as the given type, rather than using the actual runtime value's type. + public YamlMemberAttribute(Type serializeAs) : this() + { + SerializeAs = serializeAs ?? throw new ArgumentNullException(nameof(serializeAs)); + } + } +} diff --git a/tools/build/AutoNumberToStringConverter.cs b/tools/build/AutoNumberToStringConverter.cs index 4caedfaf8..1d9cde742 100644 --- a/tools/build/AutoNumberToStringConverter.cs +++ b/tools/build/AutoNumberToStringConverter.cs @@ -1,44 +1,44 @@ -using System; -using System.Globalization; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace build -{ - internal class AutoNumberToStringConverter : JsonConverter - { - public override bool CanConvert(Type typeToConvert) - { - return typeof(string) == typeToConvert; - } - - public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Number) - { - if (reader.TryGetInt64(out long number)) - { - return number.ToString(CultureInfo.InvariantCulture); - } - - if (reader.TryGetDouble(out var doubleNumber)) - { - return doubleNumber.ToString(CultureInfo.InvariantCulture); - } - } - - if (reader.TokenType == JsonTokenType.String) - { - return reader.GetString(); - } - - using var document = JsonDocument.ParseValue(ref reader); - return document.RootElement.Clone().ToString(); - } - - public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) - { - writer.WriteStringValue(value); - } - } -} +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace build +{ + internal class AutoNumberToStringConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeof(string) == typeToConvert; + } + + public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Number) + { + if (reader.TryGetInt64(out long number)) + { + return number.ToString(CultureInfo.InvariantCulture); + } + + if (reader.TryGetDouble(out var doubleNumber)) + { + return doubleNumber.ToString(CultureInfo.InvariantCulture); + } + } + + if (reader.TokenType == JsonTokenType.String) + { + return reader.GetString(); + } + + using var document = JsonDocument.ParseValue(ref reader); + return document.RootElement.Clone().ToString(); + } + + public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) + { + writer.WriteStringValue(value); + } + } +} diff --git a/tools/build/BuildDefinition.cs b/tools/build/BuildDefinition.cs index 8c1f28e67..1525d0c28 100644 --- a/tools/build/BuildDefinition.cs +++ b/tools/build/BuildDefinition.cs @@ -1,592 +1,592 @@ -using Bullseye; -using SimpleExec; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using System.Xml.Linq; -using static build.Program; -using static SimpleExec.Command; -using Host = Bullseye.Host; - -namespace build -{ - public static class BuildDefinition - { - public static GitVersion ResolveVersion(Options options, PreviousReleases releases) - { - GitVersion version; - if (ForcedVersion != null) - { - version = new GitVersion(ForcedVersion); - } - else - { - string versionJson; - try - { - versionJson = Read("dotnet", $"gitversion /nofetch{(options.Verbose ? " /diag" : "")}", BasePath); - } - catch (NonZeroExitCodeException) - { - Run("dotnet", "gitversion /nofetch /diag", BasePath); - throw; - } - - WriteVerbose(versionJson); - - if (options.Verbose) - { - // Remove extra output from versionJson - var lines = versionJson - .Split('\n') - .Select(l => l.TrimEnd('\r')) - .SkipWhile(l => !l.StartsWith('{')) - .TakeWhile(l => !l.StartsWith('}')) - .Append("}"); - - versionJson = string.Join('\n', lines); - } - - var jsonOptions = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - }; - jsonOptions.Converters.Add(new AutoNumberToStringConverter()); - - version = JsonSerializer.Deserialize(versionJson, jsonOptions); - - // Workaround to prevent issues with some consumers of the NuGet API that build - // links manually instead of following the links that come in the response. - // https://github.com/aaubry/YamlDotNet/issues/703 - version.PreReleaseLabel = version.PreReleaseLabel?.ToLowerInvariant(); - } - - if (version.CommitsSinceVersionSource > 0 && version.Equals(releases.Latest)) - { - ++version.Patch; - WriteWarning("Patch was incremented because the version was not incremented since last release."); - } - - if (version.IsPreRelease && NoPrerelease) - { - WriteWarning($"Forcing pre-release version '{version.PreReleaseLabel}' to be considered stable"); - version.PreReleaseLabel = null; - } - - WriteImportant($"Current version is {version.NuGetVersion}"); - - return version; - } - - public static void SetBuildVersion(Options options, GitVersion version) - { - switch (options.Host) - { - case Host.Appveyor: - var buildNumber = Environment.GetEnvironmentVariable("APPVEYOR_BUILD_NUMBER"); - Run("appveyor", $"UpdateBuild -Version {version.NuGetVersion}.{buildNumber}"); - break; - } - } - - public static MetadataSet SetMetadata(GitVersion version) - { - var templatePath = Path.Combine(BasePath, "YamlDotNet", "Properties", "AssemblyInfo.template"); - WriteVerbose($"Using template {templatePath}"); - - var template = File.ReadAllText(templatePath); - var assemblyInfo = template - .Replace("<%assemblyVersion%>", $"{version.Major}.0.0.0") - .Replace("<%assemblyFileVersion%>", $"{version.MajorMinorPatch}.0") - .Replace("<%assemblyInformationalVersion%>", version.NuGetVersion); - - var asssemblyInfoPath = Path.Combine(BasePath, "YamlDotNet", "Properties", "AssemblyInfo.cs"); - WriteVerbose($"Writing metadata to {asssemblyInfoPath}"); - File.WriteAllText(asssemblyInfoPath, assemblyInfo); - - return default; - } - - public static SuccessfulBuild Build(Options options, MetadataSet _) - { - var verbosity = options.Verbose ? "detailed" : "minimal"; - Run("dotnet", $"build YamlDotNet.sln --configuration Release --verbosity {verbosity}", BasePath); - - return default; - } - - public static SuccessfulUnitTests UnitTest(Options options, SuccessfulBuild _) - { - var verbosity = options.Verbose ? "detailed" : "minimal"; - Run("dotnet", $"test YamlDotNet.Test.csproj --no-build --configuration Release --verbosity {verbosity}", Path.Combine(BasePath, "YamlDotNet.Test")); - - return default; - } - - public static SuccessfulAotTests AotTest(Options options, SuccessfulBuild _) - { - var testsDir = Path.Combine(BasePath, "YamlDotNet.AotTest"); - - try - { - Run("docker", $"run --rm -v {testsDir}:/build -w /build aaubry/mono-aot bash ./run.sh"); - } - catch (NonZeroExitCodeException ex) when (options.Host == Host.Appveyor && ex.ExitCode == -1) - { - // Appveyor fails with exit code -1 for some reason... - var realExitCode = int.Parse(File.ReadAllLines(Path.Combine(testsDir, "exitcode.txt")).First(), CultureInfo.InvariantCulture); - if (realExitCode != 0) - { - throw new NonZeroExitCodeException(realExitCode); - } - } - - return default; - } - - public static NuGetPackage Pack(Options options, GitVersion version, SuccessfulUnitTests _, SuccessfulAotTests __) - { - var verbosity = options.Verbose ? "detailed" : "minimal"; - var buildDir = Path.Combine(BasePath, "YamlDotNet"); - Run("nuget", $"pack YamlDotNet.nuspec -Version {version.NuGetVersion} -OutputDirectory bin", buildDir); - - var packagePath = Path.Combine(buildDir, "bin", $"YamlDotNet.{version.NuGetVersion}.nupkg"); - return new NuGetPackage(packagePath); - } - - public static void Publish(Options options, GitVersion version, NuGetPackage package) - { - var apiKey = Environment.GetEnvironmentVariable("NUGET_API_KEY"); - if (string.IsNullOrEmpty(apiKey)) - { - throw new InvalidOperationException("NuGet API key is missing. Please set the NUGET_API_KEY environment variable."); - } - - var isSandbox = options.Host switch - { - Host.Appveyor => Environment.GetEnvironmentVariable("APPVEYOR_REPO_NAME") != "aaubry/YamlDotNet", - _ => false, - }; - - if (isSandbox) - { - WriteWarning("Skipped NuGet package publication because this is a sandbox environment"); - } - else - { - Console.WriteLine($"nuget push {package.Path} -ApiKey *** -Source https://api.nuget.org/v3/index.json"); - Run("nuget", $"push {package.Path} -ApiKey {apiKey} -Source https://api.nuget.org/v3/index.json", noEcho: true); - - if (version.IsPreRelease) - { - Console.WriteLine($"nuget delete YamlDotNet {version.NuGetVersion} -NonInteractive -ApiKey *** -Source https://api.nuget.org/v3/index.json"); - Run("nuget", $"delete YamlDotNet {version.NuGetVersion} -NonInteractive -ApiKey {apiKey} -Source https://api.nuget.org/v3/index.json", noEcho: true); - } - } - } - - public static async Task TweetRelease(GitVersion version) - { - if (version.IsPreRelease) - { - WriteWarning("Not tweeting about a pre-release."); - return; - } - - var twitterClient = new TwitterProvider( - consumerKey: Environment.GetEnvironmentVariable("TWITTER_CONSUMER_API_KEY") ?? throw new InvalidOperationException("Please set the TWITTER_CONSUMER_API_KEY environment variable."), - consumerKeySecret: Environment.GetEnvironmentVariable("TWITTER_CONSUMER_API_SECRET") ?? throw new InvalidOperationException("Please set the TWITTER_CONSUMER_API_SECRET environment variable."), - accessToken: Environment.GetEnvironmentVariable("TWITTER_ACCESS_TOKEN") ?? throw new InvalidOperationException("Please set the TWITTER_ACCESS_TOKEN environment variable."), - accessTokenSecret: Environment.GetEnvironmentVariable("TWITTER_ACCESS_TOKEN_SECRET") ?? throw new InvalidOperationException("Please set the TWITTER_ACCESS_TOKEN_SECRET environment variable.") - ); - - var message = $"#YamlDotNet {version.NuGetVersion} has just been released! https://github.com/aaubry/YamlDotNet/releases/tag/v{version.NuGetVersion}"; - var result = await twitterClient.Tweet(message); - WriteVerbose(result); - } - - public static ScaffoldedRelease ScaffoldReleaseNotes(GitVersion version, PreviousReleases releases) - { - if (version.IsPreRelease) - { - throw new InvalidOperationException("Cannot release a pre-release version."); - } - - var previousVersion = releases.Versions.First(); - - // Get the git log to scaffold the release notes - string? currentHash = null; - var commits = ReadLines("git", $"rev-list v{previousVersion}..HEAD --first-parent --reverse --pretty=tformat:%B") - .Select(l => - { - var match = Regex.Match(l, "^commit (?[a-f0-9]+)$"); - if (match.Success) - { - currentHash = match.Groups["hash"].Value; - } - return new - { - message = l, - commit = currentHash - }; - }) - .GroupBy(l => l.commit, (k, list) => new - { - commit = k, - message = list - .Skip(1) - .Select(l => Regex.Replace(l.message, @"\+semver:\s*\w+", "").Trim()) - .Where(l => !string.IsNullOrEmpty(l)) - .ToList() - }); - - var log = commits - .Select(c => c.message.Select((l, i) => $"{(i == 0 ? '-' : ' ')} {l}")) - .Select(c => string.Join(" \n", c)); - - var releaseNotes = string.Join("\n\n", log); - - WriteVerbose(releaseNotes); - - return new ScaffoldedRelease(releaseNotes); - } - - public static async Task CreateGithubRelease(GitVersion version, ScaffoldedRelease release) - { - var releaseResponse = await GitHubClient.Value.PostAsJsonAsync($"/repos/{GitHubRepository}/releases", new - { - tag_name = $"v{version.NuGetVersion}", - target_commitish = version.Sha, - name = $"Release {version.NuGetVersion}", - body = release.ReleaseNotes, - draft = true, - prerelease = version.IsPreRelease, - }); - - releaseResponse.EnsureSuccessStatusCode(); - - var releaseInfo = await releaseResponse.Content.ReadAsAsync(); - WriteImportant($"Release draft created:\n{releaseInfo.html_url}"); - } - - public static async Task LinkPullRequestsToReleases(GitVersion version) - { - if (version.IsPreRelease) - { - WriteWarning("Not linking to pre-releases."); - return; - } - - var releaseResponse = await GitHubClient.Value.GetAsync($"/repos/{GitHubRepository}/releases/tags/v{version.NuGetVersion}"); - - var release = await releaseResponse.EnsureSuccessStatusCode().Content.ReadAsAsync(); - - var linkedIssues = Regex.Matches(release.body, @"#(\d+)").Select(m => m.Groups[1].Value); - WriteVerbose($"Found the following issues / pull requests: {string.Join(",", linkedIssues)}"); - - foreach (var issueNumber in linkedIssues) - { - var prResponse = await GitHubClient.Value.GetAsync($"/repos/{GitHubRepository}/pulls/{issueNumber}"); - var isIssue = prResponse.StatusCode == HttpStatusCode.NotFound; - - var commentResponse = await GitHubClient.Value.PostAsJsonAsync($"/repos/{GitHubRepository}/issues/{issueNumber}/comments", new - { - body = isIssue - ? $"A fix for this issue has been released in [version {version.NuGetVersion}](https://github.com/{GitHubRepository}/releases/tag/v{version.NuGetVersion})." - : $"This feature has been released in [version {version.NuGetVersion}](https://github.com/{GitHubRepository}/releases/tag/v{version.NuGetVersion})." - }); - - commentResponse.EnsureSuccessStatusCode(); - } - } - - public static PreviousReleases DiscoverPreviousReleases() - { - // Find previous release - var releases = ReadLines("git", "tag --list --merged origin/master --format=\"%(refname:short)\" v*") - .Select(tag => Regex.Match(tag.TrimEnd('\r'), @"^v(?\d+)\.(?\d+)\.(?\d+)$")) - .Where(m => m.Success) - .Select(match => new Version( - int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture), - int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture), - int.Parse(match.Groups["patch"].Value, CultureInfo.InvariantCulture) - )) - .OrderByDescending(v => v) - .ToList(); - - var previousReleases = new PreviousReleases(releases); - WriteInformation($"The previous release was {previousReleases.Latest}"); - WriteVerbose("Releases:\n - " + string.Join("\n - ", releases)); - - return previousReleases; - } - - public static void Document(Options options) - { - var samplesProjectDir = Path.Combine(BasePath, "YamlDotNet.Samples"); - var samplesOutputDir = Path.Combine(BasePath, "..", "YamlDotNet.wiki"); - - var verbosity = options.Verbose ? "detailed" : "minimal"; - Run("dotnet", $"test YamlDotNet.Samples.csproj --no-build --configuration Release --verbosity {verbosity} --logger \"trx;LogFileName=TestResults.trx\"", samplesProjectDir); - - var report = XDocument.Load(Path.Combine(samplesProjectDir, "TestResults", "TestResults.trx")); - - const string ns = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010"; - - var testDefinitions = report.Root - .Element(XName.Get("TestDefinitions", ns)) - .Elements(XName.Get("UnitTest", ns)) - .Select(e => - { - var testMethod = e.Element(XName.Get("TestMethod", ns)); - - var sampleClassName = testMethod.Attribute("className").Value; - var sampleMethodName = testMethod.Attribute("name").Value; - - var testMethodAssembly = Assembly.LoadFrom(testMethod.Attribute("codeBase").Value); - - var sampleClass = testMethodAssembly - .GetType(sampleClassName, true)!; - - var sampleMethod = sampleClass - .GetMethod(sampleMethodName, BindingFlags.Instance | BindingFlags.Public) - ?? throw new InvalidOperationException($"Method {sampleClassName}.{sampleMethodName} not found"); - - var sampleAttr = sampleMethod - .GetCustomAttributes() - .Single(a => a.GetType().Name == "SampleAttribute"); - - var description = UnIndent((string)sampleAttr.GetType().GetProperty("Description")!.GetValue(sampleAttr, null)!); - - return new - { - Id = e.Attribute("id").Value, - Name = e.Attribute("name").Value, - Description = description, - Code = File.ReadAllText(Path.Combine(samplesProjectDir, $"{sampleClass.Name}.cs")), - FileName = $"Samples.{sampleClass.Name}.md", - }; - }); - - var testResults = report.Root - .Element(XName.Get("Results", ns)) - .Elements(XName.Get("UnitTestResult", ns)) - .Select(e => new - { - TestId = e.Attribute("testId").Value, - Output = e - .Element(XName.Get("Output", ns)) - ?.Element(XName.Get("StdOut", ns)) - ?.Value - }); - - var samples = testDefinitions - .GroupJoin( - testResults, - t => t.Id, - r => r.TestId, - (t, r) => new - { - t.Name, - t.Description, - t.Code, - t.FileName, - r.Single().Output, // For now we only know how to handle a single test result - } - ); - - var sampleList = new StringBuilder(); - - foreach (var sample in samples) - { - WriteInformation($"Generating sample documentation page for {sample.Name}"); - - File.WriteAllText(Path.Combine(samplesOutputDir, sample.FileName), @$" -# {sample.Name} - -{sample.Description} - -## Code - -```C# -{sample.Code} -``` - -## Output - -``` -{sample.Output} -``` -"); - - sampleList - .AppendLine($"* *[{sample.Name}]({Path.GetFileNameWithoutExtension(sample.FileName)})* ") - .AppendLine($" {sample.Description.Replace("\n", "\n ")}\n"); - } - - File.WriteAllText(Path.Combine(samplesOutputDir, "Samples.md"), $@" -# Samples - -{sampleList} - -* [Building Custom Formatters for .Net Core (Yaml Formatters)](http://www.fiyazhasan.me/building-custom-formatters-for-net-core-yaml-formatters/) by @FiyazBinHasan -"); - } - - private static string GitHubRepository => - Program.Host switch - { - Host.Appveyor => Environment.GetEnvironmentVariable("APPVEYOR_REPO_NAME"), - _ => Environment.GetEnvironmentVariable("GITHUB_REPOSITORY") - } - ?? "aaubry/YamlDotNet.Sandbox"; - - private static readonly Lazy GitHubClient = new Lazy(() => - { - var token = Environment.GetEnvironmentVariable("GITHUB_TOKEN") ?? throw new InvalidOperationException("Please set the GITHUB_TOKEN environment variable."); - - var gitHubClient = new HttpClient(new LoggerHttpHandler()) - { - BaseAddress = new Uri("https://api.github.com"), - }; - - gitHubClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json")); - gitHubClient.DefaultRequestHeaders.Add("User-Agent", GitHubRepository.Split('/')[0]); - gitHubClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("token", token); - - return gitHubClient; - }); - } - - public class GitVersion : IEquatable - { - public GitVersion() - { - } - - public GitVersion(string version) - { - var partsMatch = Regex.Match(version, @"^(?\d+).(?\d+).(?\d+)(?:-(?