From 045158a8117b04fd0df085738310d92f56152344 Mon Sep 17 00:00:00 2001 From: Marko Ristin-Kaufmann Date: Sat, 3 Oct 2026 22:23:04 +0200 Subject: [PATCH] Fix constructor headers without arguments The Python, C# and TypeScript constructors checked for no arguments and for one argument in two separate ``if``'s, so a constructor without arguments got two headers. For example, Python rendered both ``def __init__(self) -> None:`` and ``def __init__(\n self\n) -> None:`` when the constructor only called the parent constructor. We now use ``elif len(arg_codes) == 1:``. In C# and TypeScript the branch is not reachable at the moment, as the constructors without arguments have no inlined statements, and are not generated at all. We fix them nevertheless for consistency. We test it with ``Plain_marker``, which only calls ``Marker.__init__``, in the ``deep_class_hierarchy`` meta-model, and add its JSON and XML examples for the live tests. --- .../csharp/lib/_generate_types.py | 2 +- .../python/lib/_generate_types.py | 2 +- .../typescript/lib/_generate_types.py | 2 +- .../deep_class_hierarchy.py | 14 + .../expected_symbol_table.txt | 95 ++++ .../Json/Expected/PlainMarker/maximal.json | 3 + .../Json/Expected/PlainMarker/minimal.json | 3 + .../Xml/Expected/plainMarker/maximal.xml | 1 + .../Xml/Expected/plainMarker/minimal.xml | 1 + .../include/dummy/enhancing.hpp | 193 +++++++ .../include/dummy/jsonization.hpp | 34 ++ .../expected_output/include/dummy/types.hpp | 61 ++- .../include/dummy/visitation.hpp | 6 + .../include/dummy/xmlization.hpp | 34 ++ .../expected_output/src/jsonization.cpp | 337 ++++++++++++ .../expected_output/src/stringification.cpp | 6 + .../expected_output/src/types.cpp | 80 +++ .../expected_output/src/visitation.cpp | 13 + .../expected_output/src/wstringification.cpp | 6 + .../expected_output/src/xmlization.cpp | 297 +++++++++++ .../expected_output/test/common_examples.cpp | 77 +++ .../expected_output/test/common_examples.hpp | 16 + .../test/test_descent_and_descent_once.cpp | 42 ++ .../test/test_jsonization_dispatch.cpp | 55 ++ .../test_jsonization_of_concrete_classes.cpp | 56 ++ .../test/test_stringification_of_enums.cpp | 14 + .../test/test_verification.cpp | 48 ++ .../test/test_wstringification_of_enums.cpp | 14 + .../test/test_xmlization_dispatch.cpp | 68 +++ .../test_xmlization_of_concrete_classes.cpp | 51 ++ .../dummy.Tests/CommonJsonization.cs | 34 ++ .../dummy.Tests/TestCopying.cs | 47 ++ .../TestDescendAndVisitorThrough.cs | 25 + .../dummy.Tests/TestDescendOnce.cs | 15 + .../dummy.Tests/TestEnhancing.cs | 30 ++ .../TestJsonizationOfConcreteClasses.cs | 160 ++++++ .../TestJsonizationOfInterfaces.cs | 29 + .../TestXmlizationOfConcreteClasses.cs | 129 +++++ .../dummy.Tests/TestXmlizationOfInterfaces.cs | 58 ++ .../expected_output/dummy/copying.cs | 14 + .../expected_output/dummy/enhancing.cs | 71 +++ .../expected_output/dummy/jsonization.cs | 155 ++++++ .../expected_output/dummy/types.cs | 72 +++ .../expected_output/dummy/verification.cs | 9 + .../expected_output/dummy/visitation.cs | 41 ++ .../expected_output/dummy/xmlization.cs | 140 +++++ .../expected_output/enhancing/enhancing.go | 58 ++ .../enhancing/test/enhancing_test.go | 61 +++ .../jsonization/jsonization.go | 118 +++++ .../test/classes_with_descendants_test.go | 78 +++ .../jsonization/test/concrete_classes_test.go | 105 ++++ .../ourtesting/common_jsonization.go | 84 +++ .../expected_output/ourtesting/deep_equal.go | 15 + .../stringification/stringification.go | 2 + .../types/descend_test/descend_once_test.go | 24 + .../types/descend_test/descend_test.go | 24 + .../types/is_xxx_test/is_xxx_test.go | 92 ++++ .../expected_output/types/types.go | 96 ++++ .../verification/test/verification_test.go | 130 +++++ .../verification/verification.go | 22 + .../xmlization/test/concrete_classes_test.go | 126 +++++ .../expected_output/xmlization/xmlization.go | 74 +++ .../src/main/java/dummy/copying/Copying.java | 14 + .../dummy/enhancing/EnhancedPlainMarker.java | 67 +++ .../main/java/dummy/enhancing/_Wrapper.java | 20 + .../java/dummy/jsonization/Jsonization.java | 113 ++++ .../java/dummy/types/impl/PlainMarker.java | 79 +++ .../main/java/dummy/types/model/IMarker.java | 22 + .../java/dummy/types/model/IPlainMarker.java | 22 + .../java/dummy/verification/Verification.java | 10 + .../dummy/visitation/AbstractTransformer.java | 4 + .../AbstractTransformerWithContext.java | 5 + .../dummy/visitation/AbstractVisitor.java | 3 + .../AbstractVisitorWithContext.java | 4 + .../java/dummy/visitation/ITransformer.java | 3 + .../visitation/ITransformerWithContext.java | 4 + .../main/java/dummy/visitation/IVisitor.java | 3 + .../dummy/visitation/IVisitorWithContext.java | 4 + .../java/dummy/visitation/VisitorThrough.java | 9 + .../java/dummy/xmlization/Xmlization.java | 110 ++++ .../src/test/java/CommonJsonization.java | 26 + .../src/test/java/TestCopying.java | 41 ++ .../java/TestDescendAndVisitorThrough.java | 21 + .../src/test/java/TestDescendOnce.java | 13 + .../src/test/java/TestEnhancing.java | 31 ++ .../TestJsonizationOfConcreteClasses.java | 99 ++++ .../java/TestJsonizationOfInterfaces.java | 16 + .../java/TestXmlizationOfConcreteClasses.java | 93 ++++ .../test/java/TestXmlizationOfInterfaces.java | 32 ++ .../expected_output/schema.json | 28 +- .../dev/tests/common_jsonization.py | 4 + ...jsonization_of_classes_with_descendants.py | 28 + .../test_jsonization_of_concrete_classes.py | 24 + .../dev/tests/test_verification.py | 66 +++ ..._xmlization_of_classes_with_descendants.py | 50 ++ .../test_xmlization_of_concrete_classes.py | 60 +++ .../expected_output/dummy/jsonization.py | 78 +++ .../expected_output/dummy/types.py | 144 +++++ .../expected_output/dummy/verification.py | 12 + .../expected_output/dummy/xmlization.py | 500 ++++++++++++++++++ .../expected_output/src/jsonization.ts | 137 +++++ .../expected_output/src/stringification.ts | 5 + .../expected_output/src/types.ts | 345 +++++++++++- .../expected_output/src/verification.ts | 9 + .../expected_output/src/xmlization.ts | 156 ++++++ .../expected_output/test/commonJsonization.ts | 64 +++ .../expected_output/test/commonXmlization.ts | 64 +++ .../test/jsonization.common.spec.ts | 5 + .../test/jsonization.concreteClasses.spec.ts | 143 +++++ .../test/jsonization.interfaces.spec.ts | 41 ++ .../expected_output/test/types.casts.spec.ts | 131 +++++ ...types.descendAndPassThroughVisitor.spec.ts | 20 + .../test/types.descendOnce.spec.ts | 14 + .../test/types.modelType.spec.ts | 29 + .../test/types.typeMatches.spec.ts | 81 +++ .../test/xmlization.common.spec.ts | 5 + .../test/xmlization.concreteClasses.spec.ts | 72 +++ .../test/xmlization.interfaces.spec.ts | 46 ++ .../expected_output/schema.xsd | 23 + .../expected_symbol_table.txt | 64 +++ 120 files changed, 7073 insertions(+), 12 deletions(-) create mode 100644 dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Json/Expected/PlainMarker/maximal.json create mode 100644 dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Json/Expected/PlainMarker/minimal.json create mode 100644 dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Xml/Expected/plainMarker/maximal.xml create mode 100644 dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Xml/Expected/plainMarker/minimal.xml create mode 100644 dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/enhancing/EnhancedPlainMarker.java create mode 100644 dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/impl/PlainMarker.java create mode 100644 dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/model/IMarker.java create mode 100644 dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/model/IPlainMarker.java diff --git a/aas_core_codegen/csharp/lib/_generate_types.py b/aas_core_codegen/csharp/lib/_generate_types.py index 8f1acd6ff..484536e03 100644 --- a/aas_core_codegen/csharp/lib/_generate_types.py +++ b/aas_core_codegen/csharp/lib/_generate_types.py @@ -1007,7 +1007,7 @@ def _generate_constructor( if len(arg_codes) == 0: blocks.append(f"public {cls_name}()\n{{") - if len(arg_codes) == 1: + elif len(arg_codes) == 1: blocks.append(f"public {cls_name}({arg_codes[0]})\n{{") else: arg_block = ",\n".join(arg_codes) diff --git a/aas_core_codegen/python/lib/_generate_types.py b/aas_core_codegen/python/lib/_generate_types.py index 898105af5..82f43ef68 100644 --- a/aas_core_codegen/python/lib/_generate_types.py +++ b/aas_core_codegen/python/lib/_generate_types.py @@ -823,7 +823,7 @@ def _generate_constructor( if len(arg_codes) == 0: writer.write("def __init__(self) -> None:\n") - if len(arg_codes) == 1: + elif len(arg_codes) == 1: writer.write(f"def __init__(self, {arg_codes[0]}) -> None:\n") else: arg_block = ",\n".join(["self"] + arg_codes) diff --git a/aas_core_codegen/typescript/lib/_generate_types.py b/aas_core_codegen/typescript/lib/_generate_types.py index da2000c94..2c1f64e0f 100644 --- a/aas_core_codegen/typescript/lib/_generate_types.py +++ b/aas_core_codegen/typescript/lib/_generate_types.py @@ -929,7 +929,7 @@ def _generate_constructor( if len(arg_codes) == 0: blocks.append("constructor() {") - if len(arg_codes) == 1: + elif len(arg_codes) == 1: blocks.append(f"constructor({arg_codes[0]}) {{") else: arg_block = ",\n".join(arg_codes) diff --git a/dev/test_data/common_meta_models/deep_class_hierarchy.py b/dev/test_data/common_meta_models/deep_class_hierarchy.py index b74ef4cf6..698a20609 100644 --- a/dev/test_data/common_meta_models/deep_class_hierarchy.py +++ b/dev/test_data/common_meta_models/deep_class_hierarchy.py @@ -45,6 +45,20 @@ def __init__( self.details = details +@abstract +@serialization(with_model_type=True) +class Marker(DBC): + def __init__(self) -> None: + pass + + +# NOTE (mristin): +# The constructor only calls the parent constructor, but has no arguments. +class Plain_marker(Marker, DBC): + def __init__(self) -> None: + Marker.__init__(self) + + class Something(DBC): some_choice: Node something_without_choice: Branch diff --git a/dev/test_data/intermediate/common_meta_models/deep_class_hierarchy/expected_symbol_table.txt b/dev/test_data/intermediate/common_meta_models/deep_class_hierarchy/expected_symbol_table.txt index 8d2ccf48d..ff21dd749 100644 --- a/dev/test_data/intermediate/common_meta_models/deep_class_hierarchy/expected_symbol_table.txt +++ b/dev/test_data/intermediate/common_meta_models/deep_class_hierarchy/expected_symbol_table.txt @@ -494,6 +494,96 @@ SymbolTable( methods_by_name=..., method_id_set=..., invariant_id_set=...), + AbstractClass( + name='Marker', + inheritances=[], + inheritance_id_set=..., + ancestors=[], + ancestor_id_set=..., + interface=Interface( + base='Reference to AbstractClass Marker', + name='Marker', + inheritances=[], + implementers=[ + 'Reference to ConcreteClass Plain_marker'], + properties=[], + signatures=[], + description=None, + parsed=..., + properties_by_name=..., + property_id_set=...), + descendant_id_set=..., + descendants=[ + 'Reference to ConcreteClass Plain_marker'], + concrete_descendant_id_set=..., + concrete_descendants=[ + 'Reference to ConcreteClass Plain_marker'], + properties=[], + methods=[], + constructor=Constructor( + name='__init__', + arguments=[], + returns=None, + description=None, + contracts=Contracts( + preconditions=[], + snapshots=[], + postconditions=[]), + parsed=..., + arguments_by_name=..., + statements=[], + inlined_statements=[]), + invariants=[], + serialization=Serialization( + with_model_type=True), + description=None, + parsed=..., + properties_by_name=..., + property_id_set=..., + methods_by_name=..., + method_id_set=..., + invariant_id_set=...), + ConcreteClass( + name='Plain_marker', + inheritances=[ + 'Reference to AbstractClass Marker'], + inheritance_id_set=..., + ancestors=[ + 'Reference to AbstractClass Marker'], + ancestor_id_set=..., + interface=None, + descendant_id_set=..., + descendants=[], + concrete_descendant_id_set=..., + concrete_descendants=[], + properties=[], + methods=[], + constructor=Constructor( + name='__init__', + arguments=[], + returns=None, + description=None, + contracts=Contracts( + preconditions=[], + snapshots=[], + postconditions=[]), + parsed=..., + arguments_by_name=..., + statements=[ + textwrap.dedent("""\ + CallSuperConstructor( + super_name='Marker')""")], + inlined_statements=[]), + invariants=[], + serialization=Serialization( + with_model_type=True), + description=None, + parsed=..., + properties_by_name=..., + property_id_set=..., + methods_by_name=..., + method_id_set=..., + invariant_id_set=...), ConcreteClass( name='Something', inheritances=[], @@ -684,6 +774,8 @@ SymbolTable( 'Reference to our type Leaf', 'Reference to our type Blossom', 'Reference to our type Container', + 'Reference to our type Marker', + 'Reference to our type Plain_marker', 'Reference to our type Something'], enumerations=[], constrained_primitives=[], @@ -692,12 +784,15 @@ SymbolTable( 'Reference to our type Branch', 'Reference to our type Leaf', 'Reference to our type Blossom', + 'Reference to our type Marker', + 'Reference to our type Plain_marker', 'Reference to our type Something', 'Reference to our type Container'], concrete_classes=[ 'Reference to our type Branch', 'Reference to our type Leaf', 'Reference to our type Blossom', + 'Reference to our type Plain_marker', 'Reference to our type Something', 'Reference to our type Container'], named_unions=[], diff --git a/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Json/Expected/PlainMarker/maximal.json b/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Json/Expected/PlainMarker/maximal.json new file mode 100644 index 000000000..04496532c --- /dev/null +++ b/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Json/Expected/PlainMarker/maximal.json @@ -0,0 +1,3 @@ +{ + "modelType": "PlainMarker" +} \ No newline at end of file diff --git a/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Json/Expected/PlainMarker/minimal.json b/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Json/Expected/PlainMarker/minimal.json new file mode 100644 index 000000000..04496532c --- /dev/null +++ b/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Json/Expected/PlainMarker/minimal.json @@ -0,0 +1,3 @@ +{ + "modelType": "PlainMarker" +} \ No newline at end of file diff --git a/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Xml/Expected/plainMarker/maximal.xml b/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Xml/Expected/plainMarker/maximal.xml new file mode 100644 index 000000000..0a73a92d3 --- /dev/null +++ b/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Xml/Expected/plainMarker/maximal.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Xml/Expected/plainMarker/minimal.xml b/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Xml/Expected/plainMarker/minimal.xml new file mode 100644 index 000000000..0a73a92d3 --- /dev/null +++ b/dev/test_data/live_tests/common_test_data/deep_class_hierarchy/Xml/Expected/plainMarker/minimal.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/enhancing.hpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/enhancing.hpp index c9e712ba9..ffa053795 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/enhancing.hpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/enhancing.hpp @@ -79,6 +79,34 @@ std::shared_ptr< >& factory ); +template +std::shared_ptr< + types::IMarker +> Wrap( + const std::shared_ptr< + types::IMarker + >& that, + const std::function< + std::shared_ptr( + const std::shared_ptr& + ) + >& factory +); + +template +std::shared_ptr< + types::IPlainMarker +> Wrap( + const std::shared_ptr< + types::IPlainMarker + >& that, + const std::function< + std::shared_ptr( + const std::shared_ptr& + ) + >& factory +); + template std::shared_ptr< types::ISomething @@ -369,6 +397,45 @@ class EnhancedBlossom std::shared_ptr enhancement_; }; +template +class EnhancedPlainMarker + : virtual public types::IPlainMarker, + virtual public IEnhanced { + public: + types::ModelType model_type() const override { + return types::ModelType::kPlainMarker; + } + + const std::shared_ptr& enhancement() const { + return enhancement_; + } + + std::shared_ptr& mutable_enhancement() { + return enhancement_; + } + + void set_enhancement( + std::shared_ptr value + ) { + enhancement_ = std::move(value); + } + + EnhancedPlainMarker( + std::shared_ptr instance, + std::shared_ptr enhancement + ) : + instance_(instance), + enhancement_(enhancement) { + // Intentionally empty. + } + + virtual ~EnhancedPlainMarker() = default; + + private: + std::shared_ptr instance_; + std::shared_ptr enhancement_; +}; + template class EnhancedSomething : virtual public types::ISomething, @@ -611,6 +678,42 @@ std::shared_ptr WrapBlossom( ); } +/** + * Wrap \p that with an enhanced instance. + * + * \param that instance to be wrapped and enhanced + * \param factory to produce an enhancement based on an instance + * \return Enhanced instance, or `that` if no enhancement produced + * + * \tparam E type of the enhancement + */ +template +std::shared_ptr WrapPlainMarker( + const std::shared_ptr& that, + const std::function< + std::shared_ptr( + const std::shared_ptr& + ) + >& factory +) { + // We assume that we already checked whether `that` has been enhanced + // in the caller. + + // No properties to be recursively enhanced. + + std::shared_ptr enh( + factory(that) + ); + return (enh == nullptr) + ? that + : std::shared_ptr( + new EnhancedPlainMarker( + that, + enh + ) + ); +} + /** * Wrap \p that with an enhanced instance. * @@ -791,6 +894,14 @@ std::shared_ptr< factory ); break; + case types::ModelType::kPlainMarker: + return impl::WrapPlainMarker( + std::dynamic_pointer_cast< + types::IPlainMarker + >(that), + factory + ); + break; case types::ModelType::kSomething: return impl::WrapSomething( std::dynamic_pointer_cast< @@ -1024,6 +1135,88 @@ std::shared_ptr< } } +template +std::shared_ptr< + types::IMarker +> Wrap( + const std::shared_ptr< + types::IMarker + >& that, + const std::function< + std::shared_ptr( + const std::shared_ptr& + ) + >& factory +) { + impl::AssertNotEnhanced< + E, + types::IMarker + >(that); + + switch (that->model_type()) { + case types::ModelType::kPlainMarker: + return impl::WrapPlainMarker( + std::dynamic_pointer_cast< + types::IPlainMarker + >(that), + factory + ); + break; + default: + throw std::invalid_argument( + common::Concat( + "Unexpected model type: ", + std::to_string( + static_cast( + that->model_type() + ) + ) + ) + ); + break; + } +} + +template +std::shared_ptr< + types::IPlainMarker +> Wrap( + const std::shared_ptr< + types::IPlainMarker + >& that, + const std::function< + std::shared_ptr( + const std::shared_ptr& + ) + >& factory +) { + impl::AssertNotEnhanced< + E, + types::IPlainMarker + >(that); + + switch (that->model_type()) { + case types::ModelType::kPlainMarker: + return impl::WrapPlainMarker( + that, + factory + ); + break; + default: + throw std::invalid_argument( + common::Concat( + "Unexpected model type: ", + std::to_string( + static_cast( + that->model_type() + ) + ) + ) + ); + break; + } +} + template std::shared_ptr< types::ISomething diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/jsonization.hpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/jsonization.hpp index 4f43cd4ac..d6a466864 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/jsonization.hpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/jsonization.hpp @@ -182,6 +182,40 @@ common::expected< bool additional_properties = false ); +/** + * \brief Deserialize \p json value to an instance + * of types::IMarker. + * + * \param json value to be de-serialized + * \param additional_properties if not set, check that \p json contains + * no additional properties + * \return The deserialized instance, or a de-serialization error, if any. + */ +common::expected< + std::shared_ptr, + DeserializationError +> MarkerFrom( + const nlohmann::json& json, + bool additional_properties = false +); + +/** + * \brief Deserialize \p json value to an instance + * of types::IPlainMarker. + * + * \param json value to be de-serialized + * \param additional_properties if not set, check that \p json contains + * no additional properties + * \return The deserialized instance, or a de-serialization error, if any. + */ +common::expected< + std::shared_ptr, + DeserializationError +> PlainMarkerFrom( + const nlohmann::json& json, + bool additional_properties = false +); + /** * \brief Deserialize \p json value to an instance * of types::ISomething. diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/types.hpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/types.hpp index 7b3a97ada..bbdef4590 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/types.hpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/types.hpp @@ -33,8 +33,9 @@ enum class ModelType : std::uint32_t { kBranch = 0, kLeaf = 1, kBlossom = 2, - kSomething = 3, - kContainer = 4 + kPlainMarker = 3, + kSomething = 4, + kContainer = 5 }; // endregion Enumerations @@ -51,6 +52,10 @@ class ILeaf; class IBlossom; +class IMarker; + +class IPlainMarker; + class ISomething; class IContainer; @@ -125,6 +130,18 @@ class IBlossom virtual ~IBlossom() = default; }; +class IMarker + : virtual public IClass { + public: + virtual ~IMarker() = default; +}; + +class IPlainMarker + : virtual public IMarker { + public: + virtual ~IPlainMarker() = default; +}; + class ISomething : virtual public IClass { public: @@ -344,6 +361,16 @@ class Blossom std::wstring details_; }; +class PlainMarker + : public IPlainMarker { + public: + PlainMarker(); + + ModelType model_type() const override; + + ~PlainMarker() override = default; +}; + class Something : public ISomething { public: @@ -492,6 +519,36 @@ bool IsBlossom( const IClass& that ); +/** + * \brief Check whether \p that instance is of runtime type + * \ref IMarker. + * + * We use `IClass::model_type` to determine the runtime type, which is + * a bit faster than native C++'s RTTI. + * + * \param that instance to check for runtime type + * \return `true` if \p that instance is indeed + * an instance of \ref IMarker + */ +bool IsMarker( + const IClass& that +); + +/** + * \brief Check whether \p that instance is of runtime type + * \ref IPlainMarker. + * + * We use `IClass::model_type` to determine the runtime type, which is + * a bit faster than native C++'s RTTI. + * + * \param that instance to check for runtime type + * \return `true` if \p that instance is indeed + * an instance of \ref IPlainMarker + */ +bool IsPlainMarker( + const IClass& that +); + /** * \brief Check whether \p that instance is of runtime type * \ref ISomething. diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/visitation.hpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/visitation.hpp index 928a4d9e8..de7b4915d 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/visitation.hpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/visitation.hpp @@ -51,6 +51,9 @@ class IVisitor { virtual void VisitBlossom( const std::shared_ptr& that ) = 0; + virtual void VisitPlainMarker( + const std::shared_ptr& that + ) = 0; virtual void VisitSomething( const std::shared_ptr& that ) = 0; @@ -90,6 +93,9 @@ class PassThroughVisitor void VisitBlossom( const std::shared_ptr& that ) override; + void VisitPlainMarker( + const std::shared_ptr& that + ) override; void VisitSomething( const std::shared_ptr& that ) override; diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/xmlization.hpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/xmlization.hpp index f3cc68063..d715c9bbd 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/xmlization.hpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/include/dummy/xmlization.hpp @@ -150,6 +150,40 @@ common::expected< const ReadingOptions& options = {} ); +/** + * Deserialize an instance of types::IMarker from an XML + * read from the stream \p is. + * + * \param is stream to read XML from + * \param options reading options to be tweaked for special cases. The defaults should + * work in most cases. + * \return the parsed types::IMarker, or an error if any + */ +common::expected< + std::shared_ptr, + DeserializationError +> MarkerFrom( + std::istream& is, + const ReadingOptions& options = {} +); + +/** + * Deserialize an instance of types::IPlainMarker from an XML + * read from the stream \p is. + * + * \param is stream to read XML from + * \param options reading options to be tweaked for special cases. The defaults should + * work in most cases. + * \return the parsed types::IPlainMarker, or an error if any + */ +common::expected< + std::shared_ptr, + DeserializationError +> PlainMarkerFrom( + std::istream& is, + const ReadingOptions& options = {} +); + /** * Deserialize an instance of types::ISomething from an XML * read from the stream \p is. diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/jsonization.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/jsonization.cpp index 2e725af4f..d6bad41e7 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/jsonization.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/jsonization.cpp @@ -584,6 +584,10 @@ const std::unordered_map< "Blossom", types::ModelType::kBlossom }, + { + "PlainMarker", + types::ModelType::kPlainMarker + }, { "Something", types::ModelType::kSomething @@ -628,6 +632,10 @@ enum class OfBlossom : std::uint32_t { kModelType }; // enum class OfBlossom +enum class OfPlainMarker : std::uint32_t { + kModelType +}; // enum class OfPlainMarker + enum class OfSomething : std::uint32_t { kSomeChoice, kSomethingWithoutChoice @@ -704,6 +712,16 @@ const std::unordered_map< } }; +const std::unordered_map< + std::string, + OfPlainMarker +> kMapOfPlainMarker = { + { + "modelType", + OfPlainMarker::kModelType + } +}; + const std::unordered_map< std::string, OfSomething @@ -1100,6 +1118,75 @@ std::pair< bool additional_properties ); +/** + * \brief Dispatch the deserialization for an instance + * of types::IMarker. + * + * \param json value to be de-serialized + * \param additional_properties if not set, check that \p json contains + * no additional properties + * \return the deserialized instance, or an error, if any + */ +std::pair< + common::optional< + std::shared_ptr + >, + common::optional +> DeserializeMarker( + const nlohmann::json& json, + bool additional_properties +); + +/** + * \brief Parse the properties of an instance of types::IPlainMarker. + * + * The model type, if the class carries one, is expected to have been verified + * by the caller, which is what lets a dispatcher avoid verifying it twice. + * + * \param json object whose properties are to be parsed + * \param additional_properties if not set, check that \p json contains + * no additional properties + * \return the de-serialized instance, or an error, if any + */ +template < + typename T, + typename std::enable_if< + std::is_base_of::value + >::type* = nullptr +> +std::pair< + common::optional >, + common::optional +> ParsePropertiesOfPlainMarker( + const nlohmann::json& json, + bool additional_properties +); + +/** + * \brief Deserialize \p json to an instance of types::IPlainMarker. + * + * No dispatch is performed. The model type, if the class carries one, is + * verified here, since the caller has not read it. + * + * \param json value to be de-serialized + * \param additional_properties if not set, check that \p json contains + * no additional properties + * \return the de-serialized instance, or an error, if any + */ +template < + typename T, + typename std::enable_if< + std::is_base_of::value + >::type* = nullptr +> +std::pair< + common::optional >, + common::optional +> DeserializePlainMarker( + const nlohmann::json& json, + bool additional_properties +); + /** * \brief Parse the properties of an instance of types::ISomething. * @@ -1739,6 +1826,158 @@ std::pair< ); } +std::pair< + common::optional< + std::shared_ptr + >, + common::optional +> DeserializeMarker( + const nlohmann::json& json, + bool additional_properties +) { + const std::string* model_type_str; + common::optional error; + + std::tie( + model_type_str, + error + ) = GetModelTypeFrom(json); + + if (error.has_value()) { + return NoInstanceAndDeserializationError< + std::shared_ptr + >( + std::move(*error) + ); + } + + common::optional model_type( + ModelTypeFromModelTypeString(*model_type_str) + ); + + if (!model_type.has_value()) { + return NoInstanceAndDeserializationErrorWithCause< + std::shared_ptr + >( + common::Concat( + L"The model type does not correspond to any known class: ", + common::Utf8ToWstring(*model_type_str) + ) + ); + } + + switch (*model_type) { + case types::ModelType::kPlainMarker: + return ParsePropertiesOfPlainMarker< + types::IMarker + >(json, additional_properties); + default: + return NoInstanceAndDeserializationErrorWithCause< + std::shared_ptr + >( + common::Concat( + L"The dispatch to the JSON de-serialization of " + L"types::IMarker " + L"is not defined for model type: ", + common::Utf8ToWstring(*model_type_str) + ) + ); + } +} + +template < + typename T, + typename std::enable_if< + std::is_base_of::value + >::type* +> +std::pair< + common::optional >, + common::optional +> ParsePropertiesOfPlainMarker( + const nlohmann::json& json, + bool additional_properties +) { + common::optional error( + ParseProperties( + json, + properties::kMapOfPlainMarker, + additional_properties, + [&]( + properties::OfPlainMarker property, + const nlohmann::json& + ) -> common::optional { + switch (property) { + case properties::OfPlainMarker::kModelType: + // NOTE (mristin): + // The model type has been verified before the loop, so there is nothing + // left to do with it here. + return common::nullopt; + default: + throw UnexpectedPropertyLiteralError( + "properties::OfPlainMarker", + property + ); + } + } + ) + ); + + if (error.has_value()) { + return NoInstanceAndDeserializationError< + std::shared_ptr + >( + std::move(*error) + ); + } + + return std::make_pair( + common::make_optional< + std::shared_ptr + >( + // NOTE (mristin): + // We deliberately do not use std::make_shared here to avoid an unnecessary + // upcast. + new types::PlainMarker() + ), + common::nullopt + ); +} + +template < + typename T, + typename std::enable_if< + std::is_base_of::value + >::type* +> +std::pair< + common::optional >, + common::optional +> DeserializePlainMarker( + const nlohmann::json& json, + bool additional_properties +) { + common::optional error( + CheckModelType( + json, + "PlainMarker" + ) + ); + + if (error.has_value()) { + return NoInstanceAndDeserializationError< + std::shared_ptr + >( + std::move(*error) + ); + } + + return ParsePropertiesOfPlainMarker( + json, + additional_properties + ); +} + std::pair< common::optional< std::shared_ptr @@ -2072,6 +2311,40 @@ common::expected< ); } +common::expected< + std::shared_ptr, + DeserializationError +> MarkerFrom( + const nlohmann::json& json, + bool additional_properties +) { + return DeserializeFrom< + std::shared_ptr + >( + json, + additional_properties, + DeserializeMarker + ); +} + +common::expected< + std::shared_ptr, + DeserializationError +> PlainMarkerFrom( + const nlohmann::json& json, + bool additional_properties +) { + return DeserializeFrom< + std::shared_ptr + >( + json, + additional_properties, + DeserializePlainMarker< + types::IPlainMarker + > + ); +} + common::expected< std::shared_ptr, DeserializationError @@ -2534,6 +2807,16 @@ std::pair< const types::IBlossom& that ); +/** + * \brief Serialize \p that instance of types::IPlainMarker to a JSON value. + * + * \param that instance to be serialized + * \return the JSON value + */ +nlohmann::json SerializePlainMarker( + const types::IPlainMarker& that +); + /** * \brief Serialize \p that instance of types::ISomething to a JSON value. * @@ -2602,6 +2885,17 @@ std::pair< const types::ILeaf& that ); +/** + * \brief Serialize \p that instance of types::IMarker to a JSON value, + * dispatching on its model type. + * + * \param that instance to be serialized + * \return the JSON value + */ +nlohmann::json SerializeMarker( + const types::IMarker& that +); + nlohmann::json SerializeConcreteBranch( const types::IBranch& that ) { @@ -2710,6 +3004,16 @@ std::pair< ); } +nlohmann::json SerializePlainMarker( + const types::IPlainMarker& that +) { + nlohmann::json result = nlohmann::json::object(); + + result["modelType"] = "PlainMarker"; + + return result; +} + std::pair< common::optional, common::optional @@ -2914,6 +3218,33 @@ std::pair< }; } +nlohmann::json SerializeMarker( + const types::IMarker& that +) { + // NOTE (mristin): + // The dynamic casts are necessary due to virtual inheritance. Otherwise, + // we would have used static casts. + + switch (that.model_type()) { + case types::ModelType::kPlainMarker: + return SerializePlainMarker( + dynamic_cast(that) + ); + default: { + std::string message = common::Concat( + "Unexpected model type: ", + std::to_string( + static_cast( + that.model_type() + ) + ) + ); + + throw std::invalid_argument(message); + } + }; +} + std::pair< common::optional, common::optional @@ -2935,6 +3266,12 @@ std::pair< return SerializeBlossom( dynamic_cast(that) ); + case types::ModelType::kPlainMarker: + return AsFallible( + SerializePlainMarker( + dynamic_cast(that) + ) + ); case types::ModelType::kSomething: return SerializeSomething( dynamic_cast(that) diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/stringification.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/stringification.cpp index a68f0c8e4..a280f128d 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/stringification.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/stringification.cpp @@ -26,6 +26,10 @@ const std::unordered_map< "Blossom", types::ModelType::kBlossom }, + { + "PlainMarker", + types::ModelType::kPlainMarker + }, { "Something", types::ModelType::kSomething @@ -75,6 +79,8 @@ std::string to_string( return "Leaf"; case types::ModelType::kBlossom: return "Blossom"; + case types::ModelType::kPlainMarker: + return "PlainMarker"; case types::ModelType::kSomething: return "Something"; case types::ModelType::kContainer: diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/types.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/types.cpp index ff8e38c73..212c2b161 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/types.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/types.cpp @@ -193,6 +193,18 @@ void Blossom::set_details( // endregion Blossom +// region PlainMarker + +PlainMarker::PlainMarker() { + // Intentionally empty. +} + +ModelType PlainMarker::model_type() const { + return ModelType::kPlainMarker; +} + +// endregion PlainMarker + // region Something Something::Something( @@ -295,6 +307,8 @@ bool IsNode( return true; case ModelType::kBlossom: return true; + case ModelType::kPlainMarker: + return false; case ModelType::kSomething: return false; case ModelType::kContainer: @@ -321,6 +335,8 @@ bool IsBranch( return true; case ModelType::kBlossom: return true; + case ModelType::kPlainMarker: + return false; case ModelType::kSomething: return false; case ModelType::kContainer: @@ -347,6 +363,8 @@ bool IsLeaf( return true; case ModelType::kBlossom: return true; + case ModelType::kPlainMarker: + return false; case ModelType::kSomething: return false; case ModelType::kContainer: @@ -373,6 +391,64 @@ bool IsBlossom( return false; case ModelType::kBlossom: return true; + case ModelType::kPlainMarker: + return false; + case ModelType::kSomething: + return false; + case ModelType::kContainer: + return false; + default: + throw std::invalid_argument( + common::Concat( + "Unexpected model type: ", + std::to_string( + static_cast(that.model_type()) + ) + ) + ); + } +} + +bool IsMarker( + const IClass& that +) { + switch (that.model_type()) { + case ModelType::kBranch: + return false; + case ModelType::kLeaf: + return false; + case ModelType::kBlossom: + return false; + case ModelType::kPlainMarker: + return true; + case ModelType::kSomething: + return false; + case ModelType::kContainer: + return false; + default: + throw std::invalid_argument( + common::Concat( + "Unexpected model type: ", + std::to_string( + static_cast(that.model_type()) + ) + ) + ); + } +} + +bool IsPlainMarker( + const IClass& that +) { + switch (that.model_type()) { + case ModelType::kBranch: + return false; + case ModelType::kLeaf: + return false; + case ModelType::kBlossom: + return false; + case ModelType::kPlainMarker: + return true; case ModelType::kSomething: return false; case ModelType::kContainer: @@ -399,6 +475,8 @@ bool IsSomething( return false; case ModelType::kBlossom: return false; + case ModelType::kPlainMarker: + return false; case ModelType::kSomething: return true; case ModelType::kContainer: @@ -425,6 +503,8 @@ bool IsContainer( return false; case ModelType::kBlossom: return false; + case ModelType::kPlainMarker: + return false; case ModelType::kSomething: return false; case ModelType::kContainer: diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/visitation.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/visitation.cpp index 6000d72fd..a20917ce9 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/visitation.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/visitation.cpp @@ -47,6 +47,13 @@ void AbstractVisitor::Visit( >(that) ); break; + case types::ModelType::kPlainMarker: + VisitPlainMarker( + std::dynamic_pointer_cast< + types::IPlainMarker + >(that) + ); + break; case types::ModelType::kSomething: VisitSomething( std::dynamic_pointer_cast< @@ -95,6 +102,12 @@ void PassThroughVisitor::VisitBlossom( // No properties to be passed through. } +void PassThroughVisitor::VisitPlainMarker( + const std::shared_ptr& +) { + // No properties to be passed through. +} + void PassThroughVisitor::VisitSomething( const std::shared_ptr& that ) { diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/wstringification.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/wstringification.cpp index ace7b8318..19cc4d55c 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/wstringification.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/wstringification.cpp @@ -26,6 +26,10 @@ const std::unordered_map< L"Blossom", types::ModelType::kBlossom }, + { + L"PlainMarker", + types::ModelType::kPlainMarker + }, { L"Something", types::ModelType::kSomething @@ -77,6 +81,8 @@ std::wstring to_wstring( return L"Leaf"; case types::ModelType::kBlossom: return L"Blossom"; + case types::ModelType::kPlainMarker: + return L"PlainMarker"; case types::ModelType::kSomething: return L"Something"; case types::ModelType::kContainer: diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/xmlization.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/xmlization.cpp index 81daefb72..aead18411 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/xmlization.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/src/xmlization.cpp @@ -130,6 +130,37 @@ std::pair< xml_common::ReaderMergingText& reader ); +std::pair< + common::optional< + std::shared_ptr + >, + common::optional +> MarkerFromElement( + xml_common::ReaderMergingText& reader +); + +std::pair< + common::optional< + std::shared_ptr + >, + common::optional +> PlainMarkerFromElement( + xml_common::ReaderMergingText& reader +); + +template < + typename T, + typename std::enable_if< + std::is_base_of::value + >::type* = nullptr +> +std::pair< + common::optional >, + common::optional +> PlainMarkerFromSequence( + xml_common::ReaderMergingText& reader +); + std::pair< common::optional< std::shared_ptr @@ -195,6 +226,10 @@ const std::unordered_map< "blossom", types::ModelType::kBlossom }, + { + "plainMarker", + types::ModelType::kPlainMarker + }, { "something", types::ModelType::kSomething @@ -938,6 +973,10 @@ std::pair< return BlossomFromSequence< types::IClass >(a_reader); + case types::ModelType::kPlainMarker: + return PlainMarkerFromSequence< + types::IClass + >(a_reader); case types::ModelType::kSomething: return SomethingFromSequence< types::IClass @@ -1126,6 +1165,42 @@ std::pair< ); } +std::pair< + common::optional< + std::shared_ptr + >, + common::optional +> MarkerFromElement( + xml_common::ReaderMergingText& reader +) { + return DeserializeSoleFromElement< + std::shared_ptr + >( + reader, + L"IMarker", + types::ModelType::kPlainMarker, + PlainMarkerFromSequence + ); +} + +std::pair< + common::optional< + std::shared_ptr + >, + common::optional +> PlainMarkerFromElement( + xml_common::ReaderMergingText& reader +) { + return DeserializeSoleFromElement< + std::shared_ptr + >( + reader, + L"IPlainMarker", + types::ModelType::kPlainMarker, + PlainMarkerFromSequence + ); +} + std::pair< common::optional< std::shared_ptr @@ -1627,6 +1702,9 @@ enum class OfBlossom : std::uint32_t { kDetails = 3 }; // enum class OfBlossom +enum class OfPlainMarker : std::uint32_t { +}; // enum class OfPlainMarker + enum class OfSomething : std::uint32_t { kSomeChoice = 0, kSomethingWithoutChoice = 1 @@ -1697,6 +1775,13 @@ const std::unordered_map< } }; +const std::size_t kPropertyCountOfPlainMarker = 0; + +const std::unordered_map< + std::string, + OfPlainMarker +> kMapOfPlainMarker; + const std::size_t kPropertyCountOfSomething = 2; const std::unordered_map< @@ -2064,6 +2149,60 @@ std::pair< ); } +template < + typename T, + typename std::enable_if< + std::is_base_of::value + >::type* +> +std::pair< + common::optional >, + common::optional +> PlainMarkerFromSequence( + xml_common::ReaderMergingText& reader +) { + common::optional error( + ReadProperties< + properties::kPropertyCountOfPlainMarker + >( + reader, + properties::kMapOfPlainMarker, + L"IPlainMarker", + [&]( + properties::OfPlainMarker property + ) -> common::optional { + switch (property) { + default: + throw UnexpectedPropertyLiteralError( + "properties::OfPlainMarker", + property + ); + } + } + ) + ); + + if (error.has_value()) { + return NoInstanceAndDeserializationError< + std::shared_ptr + >( + std::move(*error) + ); + } + + return std::make_pair( + common::make_optional< + std::shared_ptr + >( + // NOTE (mristin): + // We deliberately do not use std::make_shared here to avoid an unnecessary + // upcast. + new types::PlainMarker() + ), + common::nullopt + ); +} + template < typename T, typename std::enable_if< @@ -2389,6 +2528,38 @@ common::expected< ); } +common::expected< + std::shared_ptr, + DeserializationError +> MarkerFrom( + std::istream& is, + const ReadingOptions& options +) { + return DeserializeFrom< + std::shared_ptr + >( + is, + options, + MarkerFromElement + ); +} + +common::expected< + std::shared_ptr, + DeserializationError +> PlainMarkerFrom( + std::istream& is, + const ReadingOptions& options +) { + return DeserializeFrom< + std::shared_ptr + >( + is, + options, + PlainMarkerFromElement + ); +} + common::expected< std::shared_ptr, DeserializationError @@ -2767,6 +2938,57 @@ common::optional SerializeBlossomPtrAsElement( xml_common::SelfClosingWriter& writer ); +/** + * \brief Serialize \p that instance by dispatching to the appropriate concrete + * serialization function. + * + * \param that instance to be serialized + * \param writer to be write to + * \return error, if any + */ +common::optional SerializeMarkerAsElement( + const types::IMarker& that, + xml_common::SelfClosingWriter& writer +); + +/** @copybrief SerializeMarkerAsElement(const types::IMarker&, xml_common::SelfClosingWriter& */ +common::optional SerializeMarkerPtrAsElement( + const std::shared_ptr& that, + xml_common::SelfClosingWriter& writer +); + +/** + * \brief Serialize \p that instance as a sequence of XML elements. + * + * Each XML element corresponds to a property. + * + * \param that instance to be serialized + * \param writer to write to + * \return error, if any + */ +common::optional SerializePlainMarkerAsSequence( + const types::IPlainMarker& that, + xml_common::SelfClosingWriter& writer +); + +/** + * Serialize \p that instance to an XML element + * ``. + * + * \param that instance to be serialized + * \return an error, if any + */ +common::optional SerializePlainMarkerAsElement( + const types::IPlainMarker& that, + xml_common::SelfClosingWriter& writer +); + +/** @copybrief SerializePlainMarkerAsElement(const types::IPlainMarker&, xml_common::SelfClosingWriter& */ +common::optional SerializePlainMarkerPtrAsElement( + const std::shared_ptr& that, + xml_common::SelfClosingWriter& writer +); + /** * \brief Serialize \p that instance as a sequence of XML elements. * @@ -3179,6 +3401,74 @@ common::optional SerializeBlossomPtrAsElement( return SerializeBlossomAsElement(*that, writer); } +common::optional SerializeMarkerAsElement( + const types::IMarker& that, + xml_common::SelfClosingWriter& writer +) { + // NOTE (mristin): + // The dynamic casts are necessary due to virtual inheritance. Otherwise, + // we would have used static casts. + + switch (that.model_type()) { + case types::ModelType::kPlainMarker: + return SerializePlainMarkerAsElement( + dynamic_cast< + const types::IPlainMarker& + >(that), + writer + ); + default: + throw std::invalid_argument( + common::Concat( + "Invalid model type: ", + stringification::to_string(that.model_type()) + ) + ); + }; +} + +common::optional SerializeMarkerPtrAsElement( + const std::shared_ptr& that, + xml_common::SelfClosingWriter& writer +) { + return SerializeMarkerAsElement(*that, writer); +} + +/** + * \brief Serialize \p that instance as a sequence of XML elements. + * + * Each XML element corresponds to a property. + * + * \param that instance to be serialized + * \param writer to write to + * \return error, if any + */ +common::optional SerializePlainMarkerAsSequence( + const types::IPlainMarker& that, + xml_common::SelfClosingWriter& writer +) { + return common::nullopt; +} + +common::optional SerializePlainMarkerAsElement( + const types::IPlainMarker& that, + xml_common::SelfClosingWriter& writer +) { + return WriteElement( + "plainMarker", + that, + writer, + SerializePlainMarkerAsSequence + ); +} + +common::optional SerializePlainMarkerPtrAsElement( + const std::shared_ptr& that, + xml_common::SelfClosingWriter& writer +) { + return SerializePlainMarkerAsElement(*that, writer); +} + /** * \brief Serialize \p that instance as a sequence of XML elements. * @@ -3327,6 +3617,13 @@ common::optional WriteClass( >(that), writer ); + case types::ModelType::kPlainMarker: + return SerializePlainMarkerAsElement( + dynamic_cast< + const types::IPlainMarker& + >(that), + writer + ); case types::ModelType::kSomething: return SerializeSomethingAsElement( dynamic_cast< diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/common_examples.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/common_examples.cpp index 684f9f15e..df02987d4 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/common_examples.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/common_examples.cpp @@ -49,6 +49,16 @@ const char* StaticTypeName< our::types::IBlossom >::name = "IBlossom"; +template<> +struct StaticTypeName< + our::types::IPlainMarker +> { + static const char* name; +}; +const char* StaticTypeName< + our::types::IPlainMarker +>::name = "IPlainMarker"; + template<> struct StaticTypeName< our::types::ISomething @@ -270,6 +280,73 @@ std::shared_ptr< ); } +std::shared_ptr< + our::types::IPlainMarker +> LoadPlainMarker( + const std::filesystem::path& path +) { + + + std::shared_ptr< + our::types::IClass + > abstract = test::common::xmlization::MustReadInstance( + path + ); + + std::shared_ptr< + our::types::IPlainMarker + > instance( + std::dynamic_pointer_cast< + our::types::IPlainMarker + >( + abstract + ) + ); + + if (instance == nullptr) { + throw std::runtime_error( + our::common::Concat( + "Failed to cast the instance to IPlainMarker from ", + path.string() + ) + ); + } + + return instance; +} + +std::shared_ptr< + our::types::IPlainMarker +> LoadMinPlainMarker() { + const std::filesystem::path path( + test::common::DetermineTestDataDir() + / "Xml" + / "Expected" + / "plainMarker" + / "minimal.xml" + ); + + return LoadPlainMarker( + path + ); +} + +std::shared_ptr< + our::types::IPlainMarker +> LoadMaxPlainMarker() { + const std::filesystem::path path( + test::common::DetermineTestDataDir() + / "Xml" + / "Expected" + / "plainMarker" + / "maximal.xml" + ); + + return LoadPlainMarker( + path + ); +} + std::shared_ptr< our::types::ISomething > LoadSomething( diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/common_examples.hpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/common_examples.hpp index 19be402b5..cecf74e9b 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/common_examples.hpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/common_examples.hpp @@ -62,6 +62,22 @@ std::shared_ptr< dummy::types::IBlossom > LoadMaxBlossom(); +/** + * Load a minimal example of IPlainMarker by + * de-serializing it from an XML file. + */ +std::shared_ptr< + dummy::types::IPlainMarker +> LoadMinPlainMarker(); + +/** + * Load a maximal example of IPlainMarker by + * de-serializing it from an XML file. + */ +std::shared_ptr< + dummy::types::IPlainMarker +> LoadMaxPlainMarker(); + /** * Load a minimal example of ISomething by * de-serializing it from an XML file. diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_descent_and_descent_once.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_descent_and_descent_once.cpp index d6d1c926c..b4622ec42 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_descent_and_descent_once.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_descent_and_descent_once.cpp @@ -219,6 +219,48 @@ TEST_CASE("Test DescentOnce over an Blossom") { AssertOrRerecordDescentOnce(instance, trace_path); } +TEST_CASE("Test Descent over an PlainMarker") { + const std::deque paths( + test::common::FindFilesBySuffixRecursively( + DetermineXmlDir() + / "Expected" + / "plainMarker", + ".xml" + ) + ); + + for (const std::filesystem::path& path : paths) { + const std::filesystem::path parent( + ( + DetermineDescentDir() + / std::filesystem::relative(path, DetermineXmlDir()) + ).parent_path() + ); + + const std::filesystem::path trace_path( + parent + / (path.filename().string() + ".trace") + ); + + AssertOrRerecordDescent(path, trace_path); + } +} + +TEST_CASE("Test DescentOnce over an PlainMarker") { + const std::shared_ptr< + our::types::IPlainMarker + > instance( + test::common::examples::LoadMaxPlainMarker() + ); + + const std::filesystem::path trace_path( + DetermineDescentOnceDir() + / "MaxPlainMarker.trace" + ); + + AssertOrRerecordDescentOnce(instance, trace_path); +} + TEST_CASE("Test Descent over an Something") { const std::deque paths( test::common::FindFilesBySuffixRecursively( diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_jsonization_dispatch.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_jsonization_dispatch.cpp index 301553f6e..fc2a4f9d3 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_jsonization_dispatch.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_jsonization_dispatch.cpp @@ -177,5 +177,60 @@ TEST_CASE("Test the round-trip of an expected ILeaf") { } } +TEST_CASE("Test the round-trip of an expected IMarker") { + const std::shared_ptr< + our::types::IPlainMarker + > concrete_instance( + test::common::examples::LoadMinPlainMarker() + ); + + const nlohmann::json json = our::jsonization::Serialize( + *concrete_instance + ); + + our::common::expected< + std::shared_ptr< + our::types::IMarker + >, + our::jsonization::DeserializationError + > instance = our::jsonization::MarkerFrom( + json + ); + + if (!instance.has_value()) { + INFO( + our::common::Concat( + "Failed to deserialize a IMarker " + "from a minimal IPlainMarker: ", + our::common::WstringToUtf8(instance.error().path.ToWstring()), + ": ", + our::common::WstringToUtf8(instance.error().cause) + ) + ) + CHECK(instance.has_value()); + } + + const nlohmann::json another_json = our::jsonization::Serialize( + **instance + ); + + const std::optional< + std::string + > patch_message = test::common::jsonization::CompareJsons( + json, + another_json + ); + if (patch_message.has_value()) { + INFO( + our::common::Concat( + "Failed to make a round-trip of a IMarker " + "over a minimal IPlainMarker: ", + *patch_message + ) + ) + CHECK(!patch_message.has_value()); + } +} + // This code has been automatically generated by aas-core-codegen. // Do NOT edit or append. diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_jsonization_of_concrete_classes.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_jsonization_of_concrete_classes.cpp index 7f4057173..3f0efde15 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_jsonization_of_concrete_classes.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_jsonization_of_concrete_classes.cpp @@ -381,6 +381,62 @@ TEST_CASE("Test the de-serialization failure on an unexpected Blossom") { } } +TEST_CASE("Test the round-trip of an expected PlainMarker") { + const std::deque paths( + test::common::FindFilesBySuffixRecursively( + DetermineJsonDir() + / "Expected" + / "PlainMarker", + ".json" + ) + ); + + for (const std::filesystem::path& path : paths) { + AssertRoundTrip< + our::types::IPlainMarker + >(path, our::jsonization::PlainMarkerFrom); + } +} + +TEST_CASE("Test the de-serialization failure on an unexpected PlainMarker") { + for ( + const std::filesystem::path& causeDir + : test::common::ListSubdirectories( + DetermineJsonDir() + / "Unexpected" + / "Unserializable" + ) + ) { + for ( + const std::filesystem::path& path + : test::common::FindFilesBySuffixRecursively( + causeDir / "PlainMarker", + ".json" + ) + ) { + const std::filesystem::path parent( + ( + DetermineErrorDir() + / std::filesystem::relative(path, DetermineJsonDir()) + ).parent_path() + ); + + const std::filesystem::path error_path( + parent + / (path.filename().string() + ".error") + ); + + AssertDeserializationFailure< + our::types::IPlainMarker + >( + path, + our::jsonization::PlainMarkerFrom, + error_path + ); + } + } +} + TEST_CASE("Test the round-trip of an expected Something") { const std::deque paths( test::common::FindFilesBySuffixRecursively( diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_stringification_of_enums.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_stringification_of_enums.cpp index fe0dbd086..28dd2c1ed 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_stringification_of_enums.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_stringification_of_enums.cpp @@ -51,6 +51,20 @@ TEST_CASE("Test ModelType round-trip") { == "Blossom" ); + REQUIRE( + our::types::ModelType::kPlainMarker + == our::stringification::MustModelTypeFromString( + "PlainMarker" + ) + ); + + REQUIRE( + our::stringification::to_string( + our::types::ModelType::kPlainMarker + ) + == "PlainMarker" + ); + REQUIRE( our::types::ModelType::kSomething == our::stringification::MustModelTypeFromString( diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_verification.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_verification.cpp index acf514442..81859ae28 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_verification.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_verification.cpp @@ -271,6 +271,54 @@ TEST_CASE("Test verification of invalid cases for Blossom") { } } +TEST_CASE("Test verification of a valid PlainMarker") { + const std::deque paths( + test::common::FindFilesBySuffixRecursively( + DetermineXmlDir() + / "Expected" + / "plainMarker", + ".xml" + ) + ); + + for (const std::filesystem::path& path : paths) { + AssertNoVerificationError(path); + } +} + +TEST_CASE("Test verification of invalid cases for PlainMarker") { + for ( + const std::filesystem::path& causeDir + : test::common::ListSubdirectories( + DetermineXmlDir() + / "Unexpected" + / "Invalid" + ) + ) { + for ( + const std::filesystem::path& path + : test::common::FindFilesBySuffixRecursively( + causeDir / "plainMarker", + ".xml" + ) + ) { + const std::filesystem::path parent( + ( + DetermineErrorDir() + / std::filesystem::relative(path, DetermineXmlDir()) + ).parent_path() + ); + + const std::filesystem::path error_path( + parent + / (path.filename().string() + ".errors") + ); + + AssertVerificationFailure(path, error_path); + } + } +} + TEST_CASE("Test verification of a valid Something") { const std::deque paths( test::common::FindFilesBySuffixRecursively( diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_wstringification_of_enums.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_wstringification_of_enums.cpp index 943483312..77e5cd4f2 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_wstringification_of_enums.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_wstringification_of_enums.cpp @@ -51,6 +51,20 @@ TEST_CASE("Test ModelType round-trip") { == L"Blossom" ); + REQUIRE( + our::types::ModelType::kPlainMarker + == our::wstringification::MustModelTypeFromWstring( + L"PlainMarker" + ) + ); + + REQUIRE( + our::wstringification::to_wstring( + our::types::ModelType::kPlainMarker + ) + == L"PlainMarker" + ); + REQUIRE( our::types::ModelType::kSomething == our::wstringification::MustModelTypeFromWstring( diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_xmlization_dispatch.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_xmlization_dispatch.cpp index 71258b927..714086707 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_xmlization_dispatch.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_xmlization_dispatch.cpp @@ -214,5 +214,73 @@ TEST_CASE("Test the round-trip of an expected ILeaf") { ); } +TEST_CASE("Test the round-trip of an expected IMarker") { + const std::shared_ptr< + our::types::IPlainMarker + > original_instance( + test::common::examples::LoadMinPlainMarker() + ); + + std::stringstream ss; + our::xmlization::Serialize( + *original_instance, + {}, + ss + ); + + const std::string original_xml = ss.str(); + + ss.seekp(0); + + our::common::expected< + std::shared_ptr, + our::xmlization::DeserializationError + > deserialized = our::xmlization::From( + ss + ); + + if (!deserialized.has_value()) { + INFO( + our::common::Concat( + "Failed to make the round-trip Serialize-Deserialize " + "a minimal instance of IPlainMarker: ", + our::common::WstringToUtf8( + deserialized.error().path.ToWstring() + ), + ": ", + our::common::WstringToUtf8( + deserialized.error().cause + ) + ) + ) + REQUIRE(deserialized.has_value()); + } + + std::shared_ptr< + our::types::IMarker + > abstract = std::dynamic_pointer_cast< + our::types::IMarker + >(deserialized.value()); + + std::stringstream another_ss; + our::xmlization::Serialize( + *abstract, + {}, + another_ss + ); + + INFO( + "Original XML and the XML at the end of " + "the chain Serialize-Deserialize-Serialize for " + "a minimal instance of IPlainMarker " + "serialized as IMarker " + "must coincide" + ) + REQUIRE( + original_xml + == another_ss.str() + ); +} + // This code has been automatically generated by aas-core-codegen. // Do NOT edit or append. diff --git a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_xmlization_of_concrete_classes.cpp b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_xmlization_of_concrete_classes.cpp index f1d8e0fc7..4696169ed 100644 --- a/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_xmlization_of_concrete_classes.cpp +++ b/dev/test_data/main/cpp/expected/deep_class_hierarchy/expected_output/test/test_xmlization_of_concrete_classes.cpp @@ -241,6 +241,57 @@ TEST_CASE("Test the de-serialization failure on an unexpected Blossom") { } } +TEST_CASE("Test the round-trip of an expected PlainMarker") { + const std::deque paths( + test::common::FindFilesBySuffixRecursively( + DetermineXmlDir() + / "Expected" + / "plainMarker", + ".xml" + ) + ); + + for (const std::filesystem::path &path : paths) { + AssertRoundTrip(path); + } +} + +TEST_CASE("Test the de-serialization failure on an unexpected PlainMarker") { + for ( + const std::filesystem::path& causeDir + : test::common::ListSubdirectories( + DetermineXmlDir() + / "Unexpected" + / "Unserializable" + ) + ) { + for ( + const std::filesystem::path& path + : test::common::FindFilesBySuffixRecursively( + causeDir / "plainMarker", + ".xml" + ) + ) { + const std::filesystem::path parent( + ( + DetermineErrorDir() + / std::filesystem::relative(path, DetermineXmlDir()) + ).parent_path() + ); + + const std::filesystem::path error_path( + parent + / (path.filename().string() + ".error") + ); + + AssertDeserializationFailure( + path, + error_path + ); + } + } +} + TEST_CASE("Test the round-trip of an expected Something") { const std::deque paths( test::common::FindFilesBySuffixRecursively( diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/CommonJsonization.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/CommonJsonization.cs index ea641c692..40587bf54 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/CommonJsonization.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/CommonJsonization.cs @@ -116,6 +116,40 @@ public static Our.Blossom LoadMinimalBlossom() return instance; } // public static Our.Blossom LoadMinimalBlossom + public static Our.PlainMarker LoadMaximalPlainMarker() + { + string path = Path.Combine( + Our.Tests.Common.TestDataDir, + "Json", + "Expected", + "PlainMarker", + "maximal.json"); + + var node = Our.Tests.CommonJson.ReadFromFile(path); + + var instance = Our.Jsonization.Deserialize.PlainMarkerFrom( + node); + + return instance; + } // public static Our.PlainMarker LoadMaximalPlainMarker + + public static Our.PlainMarker LoadMinimalPlainMarker() + { + string path = Path.Combine( + Our.Tests.Common.TestDataDir, + "Json", + "Expected", + "PlainMarker", + "minimal.json"); + + var node = Our.Tests.CommonJson.ReadFromFile(path); + + var instance = Our.Jsonization.Deserialize.PlainMarkerFrom( + node); + + return instance; + } // public static Our.PlainMarker LoadMinimalPlainMarker + public static Our.Something LoadMaximalSomething() { string path = Path.Combine( diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestCopying.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestCopying.cs index fe372c10e..bfbb977fb 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestCopying.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestCopying.cs @@ -82,6 +82,18 @@ public override bool TransformBlossom( && that.Details == casted.Details); } + public override bool TransformPlainMarker( + Our.IPlainMarker that, + Our.IClass other) + { + if (!(other is Our.PlainMarker casted) || ReferenceEquals(that, other)) + { + return false; + } + + return true; + } + public override bool TransformSomething( Our.ISomething that, Our.IClass other) @@ -152,6 +164,13 @@ private static bool BlossomShallowEquals( && that.Details == other.Details); } + private static bool PlainMarkerShallowEquals( + Our.PlainMarker that, + Our.PlainMarker other) + { + return true; + } + private static bool SomethingShallowEquals( Our.Something that, Our.Something other) @@ -252,6 +271,34 @@ public void Test_Blossom_deep_copy() "Blossom"); } // public void Test_Blossom_deep_copy + [Test] + public void Test_PlainMarker_shallow_copy() + { + Our.PlainMarker instance = ( + Our.Tests.CommonJsonization.LoadMaximalPlainMarker()); + + var instanceCopy = Our.Copying.Shallow(instance); + + Assert.IsTrue( + PlainMarkerShallowEquals( + instance, instanceCopy), + "PlainMarker"); + } // public void Test_PlainMarker_shallow_copy + + [Test] + public void Test_PlainMarker_deep_copy() + { + Our.PlainMarker instance = ( + Our.Tests.CommonJsonization.LoadMaximalPlainMarker()); + + var instanceCopy = Our.Copying.Deep(instance); + + Assert.IsTrue( + DeepCopyCheckerInstance.Transform( + instance, instanceCopy), + "PlainMarker"); + } // public void Test_PlainMarker_deep_copy + [Test] public void Test_Something_shallow_copy() { diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestDescendAndVisitorThrough.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestDescendAndVisitorThrough.cs index c7f7cfbaf..a7c1820ac 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestDescendAndVisitorThrough.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestDescendAndVisitorThrough.cs @@ -168,6 +168,31 @@ public void Test_Descend_against_VisitorThrough_for_Blossom() instance); } // public void Test_Descend_against_VisitorThrough_for_Blossom + [Test] + public void Test_Descend_of_PlainMarker() + { + Our.PlainMarker instance = ( + Our.Tests.CommonJsonization.LoadMaximalPlainMarker()); + + CompareOrRerecordTrace( + instance, + Path.Combine( + Our.Tests.Common.TestDataDir, + "Descend", + "PlainMarker", + "maximal.json.trace")); + } // public void Test_Descend_of_PlainMarker + + [Test] + public void Test_Descend_against_VisitorThrough_for_PlainMarker() + { + Our.PlainMarker instance = ( + Our.Tests.CommonJsonization.LoadMaximalPlainMarker()); + + AssertDescendAndVisitorThroughSame( + instance); + } // public void Test_Descend_against_VisitorThrough_for_PlainMarker + [Test] public void Test_Descend_of_Something() { diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestDescendOnce.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestDescendOnce.cs index 8e35397ff..5e9b1b76e 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestDescendOnce.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestDescendOnce.cs @@ -102,6 +102,21 @@ public void Test_Blossom() "maximal.json.trace")); } // public void Test_Blossom + [Test] + public void Test_PlainMarker() + { + Our.PlainMarker instance = ( + Our.Tests.CommonJsonization.LoadMaximalPlainMarker()); + + CompareOrRerecordTrace( + instance, + Path.Combine( + Our.Tests.Common.TestDataDir, + "DescendOnce", + "PlainMarker", + "maximal.json.trace")); + } // public void Test_PlainMarker + [Test] public void Test_Something() { diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestEnhancing.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestEnhancing.cs index 566009e5b..3caf11b73 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestEnhancing.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestEnhancing.cs @@ -130,6 +130,36 @@ public void Test_Blossom() Assert.AreEqual(idSet.Count, idSet.Max()); } // public void Test_Blossom + [Test] + public void Test_PlainMarker() + { + var instance = ( + Our.Tests.CommonJsonization.LoadMaximalPlainMarker() + ); + + var enhancer = CreateEnhancer(); + + Assert.IsNull(enhancer.Unwrap(instance)); + + var wrapped = enhancer.Wrap(instance); + Assert.IsNotNull(wrapped); + + var idSet = new HashSet(); + + idSet.Add(enhancer.MustUnwrap(wrapped).SomeCustomId); + idSet.UnionWith( + wrapped + .Descend() + .Select( + (descendant) => + enhancer.MustUnwrap(descendant).SomeCustomId + ) + ); + + Assert.AreEqual(1, idSet.Min()); + Assert.AreEqual(idSet.Count, idSet.Max()); + } // public void Test_PlainMarker + [Test] public void Test_Something() { diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestJsonizationOfConcreteClasses.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestJsonizationOfConcreteClasses.cs index e13d536bd..e2c10dab7 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestJsonizationOfConcreteClasses.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestJsonizationOfConcreteClasses.cs @@ -573,6 +573,166 @@ string causeDir in } } // public void Test_Blossom_verification_fail + [Test] + public void Test_PlainMarker_ok() + { + var paths = Directory.GetFiles( + Path.Combine( + Our.Tests.Common.TestDataDir, + "Json", + "Expected", + "PlainMarker" + ), + "*.json", + System.IO.SearchOption.AllDirectories).ToList(); + paths.Sort(); + + foreach (var path in paths) + { + var node = Our.Tests.CommonJson.ReadFromFile(path); + + var instance = Our.Jsonization.Deserialize.PlainMarkerFrom( + node); + + var errors = Our.Verification.Verify(instance).ToList(); + Our.Tests.Common.AssertNoVerificationErrors(errors, path); + + AssertSerializeDeserializeEqualsOriginal( + node, instance, path); + } + } // public void Test_PlainMarker_ok + + [Test] + public void Test_PlainMarker_deserialization_from_non_object_fail() + { + var node = Nodes.JsonValue.Create("INVALID") + ?? throw new System.InvalidOperationException( + "Unexpected failure of the node creation"); + + Our.Jsonization.Exception? exception = null; + try + { + var _ = Our.Jsonization.Deserialize.PlainMarkerFrom( + node); + } + catch (Our.Jsonization.Exception observedException) + { + exception = observedException; + } + + if (exception == null) + { + throw new AssertionException("Expected an exception, but got none"); + } + + if ( + !exception.Message.StartsWith( + "Expected a JsonObject representing PlainMarker, but got ")) + { + throw new AssertionException( + $"Unexpected exception message: {exception.Message}"); + } + } // public void Test_PlainMarker_deserialization_from_non_object_fail + + [Test] + public void Test_PlainMarker_deserialization_fail() + { + foreach ( + string causeDir in + Directory.GetDirectories( + Path.Combine( + Our.Tests.Common.TestDataDir, + "Json", + "Unexpected", + "Unserializable" + ) + ) + ) + { + string clsDir = Path.Combine( + causeDir, + "PlainMarker" + ); + + if (!Directory.Exists(clsDir)) + { + // No examples of PlainMarker for the failure cause. + continue; + } + + var paths = Directory.GetFiles( + clsDir, + "*.json", + System.IO.SearchOption.AllDirectories).ToList(); + paths.Sort(); + + foreach (var path in paths) + { + var node = Our.Tests.CommonJson.ReadFromFile(path); + + Our.Jsonization.Exception? exception = null; + try + { + var _ = Our.Jsonization.Deserialize.PlainMarkerFrom( + node); + } + catch (Our.Jsonization.Exception observedException) + { + exception = observedException; + } + + AssertEqualsExpectedOrRerecordDeserializationException( + exception, path); + } + } + } // public void Test_PlainMarker_deserialization_fail + + [Test] + public void Test_PlainMarker_verification_fail() + { + foreach ( + string causeDir in + Directory.GetDirectories( + Path.Combine( + Our.Tests.Common.TestDataDir, + "Json", + "Unexpected", + "Invalid" + ) + ) + ) + { + string clsDir = Path.Combine( + causeDir, + "PlainMarker" + ); + + if (!Directory.Exists(clsDir)) + { + // No examples of PlainMarker for the failure cause. + continue; + } + + var paths = Directory.GetFiles( + clsDir, + "*.json", + System.IO.SearchOption.AllDirectories).ToList(); + paths.Sort(); + + foreach (var path in paths) + { + var node = Our.Tests.CommonJson.ReadFromFile(path); + + var instance = Our.Jsonization.Deserialize.PlainMarkerFrom( + node); + + var errors = Our.Verification.Verify(instance).ToList(); + Our.Tests.Common.AssertEqualsExpectedOrRerecordVerificationErrors( + errors, path); + } + } + } // public void Test_PlainMarker_verification_fail + [Test] public void Test_Something_ok() { diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestJsonizationOfInterfaces.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestJsonizationOfInterfaces.cs index f37432e6a..5ee6f4bcc 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestJsonizationOfInterfaces.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestJsonizationOfInterfaces.cs @@ -242,6 +242,35 @@ public void Test_round_trip_ILeaf_from_Leaf() ); } } // void Test_round_trip_ILeaf_from_Leaf + + [Test] + public void Test_round_trip_IMarker_from_PlainMarker() + { + var instance = Our.Tests.CommonJsonization.LoadMaximalPlainMarker(); + + var jsonObject = Our.Jsonization.Serialize.ToJsonObject(instance); + + var anotherInstance = Our.Jsonization.Deserialize.IMarkerFrom( + jsonObject); + + var anotherJsonObject = Our.Jsonization.Serialize.ToJsonObject( + anotherInstance); + + Our.Tests.CommonJson.CheckJsonNodesEqual( + jsonObject, + anotherJsonObject, + out Our.Reporting.Error? error); + + if (error != null) + { + Assert.Fail( + "When we serialize the complete instance of PlainMarker " + + "as IMarker, we get an error in the round trip: " + + $"{Reporting.GenerateJsonPath(error.PathSegments)}: " + + error.Cause + ); + } + } // void Test_round_trip_IMarker_from_PlainMarker } // class TestJsonizationOfInterfaces } // namespace dummy.Tests diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestXmlizationOfConcreteClasses.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestXmlizationOfConcreteClasses.cs index d0e50152b..dc96a375b 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestXmlizationOfConcreteClasses.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestXmlizationOfConcreteClasses.cs @@ -554,6 +554,135 @@ string causeDir in } } // public void Test_Blossom_verification_fail + [Test] + public void Test_PlainMarker_ok() + { + var paths = Directory.GetFiles( + Path.Combine( + Our.Tests.Common.TestDataDir, + "Xml", + "Expected", + "plainMarker" + ), + "*.xml", + System.IO.SearchOption.AllDirectories).ToList(); + paths.Sort(); + + foreach (var path in paths) + { + using var xmlReader = System.Xml.XmlReader.Create(path); + + var instance = Our.Xmlization.Deserialize.PlainMarkerFrom( + xmlReader); + + var errors = Our.Verification.Verify(instance).ToList(); + Our.Tests.Common.AssertNoVerificationErrors(errors, path); + + AssertSerializeDeserializeEqualsOriginal( + instance, path); + } + } // public void Test_PlainMarker_ok + + [Test] + public void Test_PlainMarker_deserialization_fail() + { + foreach ( + string causeDir in + Directory.GetDirectories( + Path.Combine( + Our.Tests.Common.TestDataDir, + "Xml", + "Unexpected", + "Unserializable" + ) + ) + ) + { + string clsDir = Path.Combine( + causeDir, + "plainMarker" + ); + + if (!Directory.Exists(clsDir)) + { + // No examples of PlainMarker for the failure cause. + continue; + } + + var paths = Directory.GetFiles( + clsDir, + "*.xml", + System.IO.SearchOption.AllDirectories).ToList(); + paths.Sort(); + + foreach (var path in paths) + { + using var xmlReader = System.Xml.XmlReader.Create(path); + + Our.Xmlization.Exception? exception = null; + + try + { + _ = Our.Xmlization.Deserialize.PlainMarkerFrom( + xmlReader); + } + catch (Our.Xmlization.Exception observedException) + { + exception = observedException; + } + + AssertEqualsExpectedOrRerecordDeserializationException( + exception, path); + } + } + } // public void Test_PlainMarker_deserialization_fail + + [Test] + public void Test_PlainMarker_verification_fail() + { + foreach ( + string causeDir in + Directory.GetDirectories( + Path.Combine( + Our.Tests.Common.TestDataDir, + "Xml", + "Unexpected", + "Invalid" + ) + ) + ) + { + string clsDir = Path.Combine( + causeDir, + "plainMarker" + ); + + if (!Directory.Exists(clsDir)) + { + // No examples of PlainMarker for the failure cause. + continue; + } + + var paths = Directory.GetFiles( + clsDir, + "*.xml", + System.IO.SearchOption.AllDirectories).ToList(); + paths.Sort(); + + foreach (var path in paths) + { + using var xmlReader = System.Xml.XmlReader.Create(path); + + var instance = Our.Xmlization.Deserialize.PlainMarkerFrom( + xmlReader); + + var errors = Our.Verification.Verify(instance).ToList(); + Our.Tests.Common.AssertEqualsExpectedOrRerecordVerificationErrors( + errors, path); + } + } + } // public void Test_PlainMarker_verification_fail + [Test] public void Test_Something_ok() { diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestXmlizationOfInterfaces.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestXmlizationOfInterfaces.cs index 9feb77586..cc4f38ba9 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestXmlizationOfInterfaces.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy.Tests/TestXmlizationOfInterfaces.cs @@ -474,6 +474,64 @@ public void Test_round_trip_ILeaf_from_Leaf() // Compare Assert.AreEqual(outputText, anotherOutputBuilder.ToString()); } // void Test_round_trip_ILeaf_from_Leaf + + [Test] + public void Test_round_trip_IMarker_from_PlainMarker() + { + // We load from JSON here just to jump-start the round trip. + // The round-trip goes then over XML. + var instance = Our.Tests.CommonJsonization.LoadMaximalPlainMarker(); + + // The round-trip starts here. + var outputBuilder = new System.Text.StringBuilder(); + + // Serialize to XML + { + using var xmlWriter = System.Xml.XmlWriter.Create( + outputBuilder, + new System.Xml.XmlWriterSettings() + { + Encoding = System.Text.Encoding.UTF8, + OmitXmlDeclaration = true + }); + + Our.Xmlization.Serialize.To( + instance, + xmlWriter); + } + + // De-serialize from XML + string outputText = outputBuilder.ToString(); + + using var outputReader = new System.IO.StringReader(outputText); + + using var xmlReader = System.Xml.XmlReader.Create( + outputReader, + new System.Xml.XmlReaderSettings()); + + var anotherInstance = Our.Xmlization.Deserialize.IMarkerFrom( + xmlReader); + + // Serialize back to XML + var anotherOutputBuilder = new System.Text.StringBuilder(); + + { + using var anotherXmlWriter = System.Xml.XmlWriter.Create( + anotherOutputBuilder, + new System.Xml.XmlWriterSettings() + { + Encoding = System.Text.Encoding.UTF8, + OmitXmlDeclaration = true + }); + + Our.Xmlization.Serialize.To( + anotherInstance, + anotherXmlWriter); + } + + // Compare + Assert.AreEqual(outputText, anotherOutputBuilder.ToString()); + } // void Test_round_trip_IMarker_from_PlainMarker } // class TestXmlizationOfInterfaces } // namespace dummy.Tests diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/copying.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/copying.cs index 3ca6522e1..04e45b444 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/copying.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/copying.cs @@ -72,6 +72,13 @@ Our.IBlossom that that.Details); } + public override Our.IClass TransformPlainMarker( + Our.IPlainMarker that + ) + { + return new Our.PlainMarker(); + } + public override Our.IClass TransformSomething( Our.ISomething that ) @@ -124,6 +131,13 @@ Our.IBlossom that ); } + public override Our.IClass TransformPlainMarker( + Our.IPlainMarker that + ) + { + return new Our.PlainMarker(); + } + public override Our.IClass TransformSomething( Our.ISomething that ) diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/enhancing.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/enhancing.cs index b6c49b29a..cb18aada0 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/enhancing.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/enhancing.cs @@ -236,6 +236,57 @@ TContext context } } + public class EnhancedPlainMarker + : Enhanced, Our.IPlainMarker + where TEnhancement : class + { + private readonly Our.IPlainMarker _instance; + + public EnhancedPlainMarker( + Our.IPlainMarker instance, + TEnhancement enhancement + ) : base(enhancement) + { + _instance = instance; + } + + public IEnumerable DescendOnce() + { + return _instance.DescendOnce(); + } + + public IEnumerable Descend() + { + return _instance.Descend(); + } + + public void Accept(Our.Visitation.IVisitor visitor) + { + visitor.VisitPlainMarker(_instance); + } + + public void Accept( + Visitation.IVisitorWithContext visitor, + TContext context + ) + { + visitor.VisitPlainMarker(_instance, context); + } + + public T Transform(Visitation.ITransformer transformer) + { + return transformer.TransformPlainMarker(_instance); + } + + public T Transform( + Visitation.ITransformerWithContext transformer, + TContext context + ) + { + return transformer.TransformPlainMarker(_instance, context); + } + } + public class EnhancedSomething : Enhanced, Our.ISomething where TEnhancement : class @@ -435,6 +486,26 @@ Our.IBlossom that ); } + public override Our.IClass TransformPlainMarker( + Our.IPlainMarker that + ) + { + if (that is Enhanced) + { + throw new System.ArgumentException( + $"The instance has been already enhanced: {that}" + ); + } + + var enhancement = _enhancementFactory(that); + return (enhancement == null) + ? that + : new EnhancedPlainMarker( + that, + enhancement + ); + } + public override Our.IClass TransformSomething( Our.ISomething that ) diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/jsonization.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/jsonization.cs index 0f88a620c..4c476b813 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/jsonization.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/jsonization.cs @@ -704,6 +704,103 @@ internal static Our.Blossom BlossomFrom( "Unexpected null, had to be handled before")); } // internal static BlossomFrom + /// + /// Deserialize an instance of IMarker by dispatching + /// based on modelType property of the . + /// + /// JSON node to be parsed + /// Error, if any, during the deserialization + [CodeAnalysis.SuppressMessage("ReSharper", "InconsistentNaming")] + public static Our.IMarker IMarkerFrom( + Nodes.JsonNode? node, + out Reporting.Error? error) + { + Nodes.JsonObject? obj = node as Nodes.JsonObject; + if (obj == null) + { + error = new Reporting.Error( + $"Expected a JsonObject representing IMarker, but got {Describe(node)}"); + return default!; + } + + string modelType = ModelTypeFrom(obj, out error); + if (error != null) + { + return default!; + } + + switch (modelType) + { + case "PlainMarker": + return PlainMarkerFrom( + node, out error); + default: + error = new Reporting.Error( + $"Unexpected model type for IMarker: {modelType}"); + return default!; + } + } // public static Our.IMarker IMarkerFrom + + /// + /// Deserialize an instance of PlainMarker from . + /// + /// JSON node to be parsed + /// Error, if any, during the deserialization + internal static Our.PlainMarker PlainMarkerFrom( + Nodes.JsonNode? node, + out Reporting.Error? error) + { + error = null; + + Nodes.JsonObject? obj = node as Nodes.JsonObject; + if (obj == null) + { + error = new Reporting.Error( + $"Expected a JsonObject representing PlainMarker, but got {Describe(node)}"); + return default!; + } + + string? modelType = null; + + foreach (var keyValue in obj) + { + switch (keyValue.Key) + { + case "modelType": + modelType = StringFrom( + keyValue.Value, out error); + if (error == null && modelType != "PlainMarker") + { + error = new Reporting.Error( + "Expected the model type 'PlainMarker', " + + $"but got {modelType}"); + } + break; + default: + error = new Reporting.Error( + $"Unexpected property: {keyValue.Key}"); + return default!; + } + + if (error != null) + { + error.PrependSegment( + new Reporting.NameSegment( + keyValue.Key)); + return default!; + } + } + + if (modelType == null) + { + error = new Reporting.Error( + "Required property \"modelType\" is missing"); + return default!; + } + + return new Our.PlainMarker(); + } // internal static PlainMarkerFrom + /// /// Deserialize an instance of Something from . /// @@ -1055,6 +1152,53 @@ public static Our.Blossom BlossomFrom( return result; } + /// + /// Deserialize an instance of IMarker from . + /// + /// JSON node to be parsed + /// + /// Thrown when is not a valid JSON + /// representation of IMarker. + /// + [CodeAnalysis.SuppressMessage("ReSharper", "InconsistentNaming")] + public static Our.IMarker IMarkerFrom( + Nodes.JsonNode node) + { + Our.IMarker result = DeserializeImplementation.IMarkerFrom( + node, + out Reporting.Error? error); + if (error != null) + { + throw new Jsonization.Exception( + Reporting.GenerateJsonPath(error.PathSegments), + error.Cause); + } + return result; + } + + /// + /// Deserialize an instance of PlainMarker from . + /// + /// JSON node to be parsed + /// + /// Thrown when is not a valid JSON + /// representation of PlainMarker. + /// + public static Our.PlainMarker PlainMarkerFrom( + Nodes.JsonNode node) + { + Our.PlainMarker result = DeserializeImplementation.PlainMarkerFrom( + node, + out Reporting.Error? error); + if (error != null) + { + throw new Jsonization.Exception( + Reporting.GenerateJsonPath(error.PathSegments), + error.Cause); + } + return result; + } + /// /// Deserialize an instance of Something from . /// @@ -1303,6 +1447,17 @@ Our.IBlossom that return result; } + public override Nodes.JsonObject TransformPlainMarker( + Our.IPlainMarker that + ) + { + var result = new Nodes.JsonObject(); + + result["modelType"] = "PlainMarker"; + + return result; + } + public override Nodes.JsonObject TransformSomething( Our.ISomething that ) diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/types.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/types.cs index 7cd30d20b..22137b9ce 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/types.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/types.cs @@ -309,6 +309,78 @@ public Blossom( } } + public interface IMarker : IClass + { + // Intentionally empty. + } + + public interface IPlainMarker : IMarker + { + // Intentionally empty. + } + + public class PlainMarker : IPlainMarker + { + /// + /// Iterate over all the class instances referenced from this instance + /// without further recursion. + /// + public IEnumerable DescendOnce() + { + // No descendable properties + yield break; + } + + /// + /// Iterate recursively over all the class instances referenced from this instance. + /// + public IEnumerable Descend() + { + // No descendable properties + yield break; + } + + /// + /// Accept the to visit this instance + /// for double dispatch. + /// + public void Accept(Visitation.IVisitor visitor) + { + visitor.VisitPlainMarker(this); + } + + /// + /// Accept the visitor to visit this instance for double dispatch + /// with the . + /// + public void Accept( + Visitation.IVisitorWithContext visitor, + TContext context) + { + visitor.VisitPlainMarker(this, context); + } + + /// + /// Accept the to transform this instance + /// for double dispatch. + /// + public T Transform(Visitation.ITransformer transformer) + { + return transformer.TransformPlainMarker(this); + } + + /// + /// Accept the to visit this instance + /// for double dispatch with the . + /// + public T Transform( + Visitation.ITransformerWithContext transformer, + TContext context) + { + return transformer.TransformPlainMarker(this, context); + } + } + public interface ISomething : IClass { public INode SomeChoice { get; set; } diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/verification.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/verification.cs index 9213aa777..679c30f13 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/verification.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/verification.cs @@ -73,6 +73,15 @@ Our.IBlossom that yield break; } + [CodeAnalysis.SuppressMessage("ReSharper", "NegativeEqualityExpression")] + public override IEnumerable TransformPlainMarker( + Our.IPlainMarker that + ) + { + // No verification has been defined for PlainMarker. + yield break; + } + [CodeAnalysis.SuppressMessage("ReSharper", "NegativeEqualityExpression")] public override IEnumerable TransformSomething( Our.ISomething that diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/visitation.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/visitation.cs index b43401bd6..5be006409 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/visitation.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/visitation.cs @@ -27,6 +27,9 @@ ILeaf that public void VisitBlossom( IBlossom that ); + public void VisitPlainMarker( + IPlainMarker that + ); public void VisitSomething( ISomething that ); @@ -83,6 +86,17 @@ IBlossom that } } + public virtual void VisitPlainMarker( + IPlainMarker that + ) + { + // Just descend through, do nothing with that + foreach (var something in that.DescendOnce()) + { + Visit(something); + } + } + public virtual void VisitSomething( ISomething that ) @@ -124,6 +138,9 @@ ILeaf that public abstract void VisitBlossom( IBlossom that ); + public abstract void VisitPlainMarker( + IPlainMarker that + ); public abstract void VisitSomething( ISomething that ); @@ -156,6 +173,10 @@ public void VisitBlossom( IBlossom that, TContext context ); + public void VisitPlainMarker( + IPlainMarker that, + TContext context + ); public void VisitSomething( ISomething that, TContext context @@ -190,6 +211,10 @@ public abstract void VisitBlossom( IBlossom that, TContext context ); + public abstract void VisitPlainMarker( + IPlainMarker that, + TContext context + ); public abstract void VisitSomething( ISomething that, TContext context @@ -222,6 +247,9 @@ ILeaf that public T TransformBlossom( IBlossom that ); + public T TransformPlainMarker( + IPlainMarker that + ); public T TransformSomething( ISomething that ); @@ -254,6 +282,10 @@ public abstract T TransformBlossom( IBlossom that ); + public abstract T TransformPlainMarker( + IPlainMarker that + ); + public abstract T TransformSomething( ISomething that ); @@ -289,6 +321,10 @@ public T TransformBlossom( IBlossom that, TContext context ); + public T TransformPlainMarker( + IPlainMarker that, + TContext context + ); public T TransformSomething( ISomething that, TContext context @@ -333,6 +369,11 @@ public abstract T TransformBlossom( TContext context ); + public abstract T TransformPlainMarker( + IPlainMarker that, + TContext context + ); + public abstract T TransformSomething( ISomething that, TContext context diff --git a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/xmlization.cs b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/xmlization.cs index 56f23a775..762e829d1 100644 --- a/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/xmlization.cs +++ b/dev/test_data/main/csharp/expected/deep_class_hierarchy/expected_output/dummy/xmlization.cs @@ -375,6 +375,13 @@ out Reporting.Error? error AtElement( BlossomFromSequence, "blossom")); + /// + /// Read an instance of class PlainMarker from its XML element. + /// + internal static readonly ElementReader PlainMarkerFromElement = ( + AtElement( + PlainMarkerFromSequence, "plainMarker")); + /// /// Read an instance of class Something from its XML element. /// @@ -916,6 +923,50 @@ internal static Our.Blossom BlossomFromSequence( "Unexpected null, had to be handled before")); } // internal static Our.Blossom? BlossomFromSequence + /// + /// Deserialize an instance of IMarker from an XML element. + /// + [CodeAnalysis.SuppressMessage("ReSharper", "InconsistentNaming")] + internal static Our.IMarker IMarkerFromElement( + Xml.XmlReader reader, + out Reporting.Error? error) + { + string elementName = XmlCommon.PeekElementName( + reader, out error); + if (error != null) + { + return default!; + } + + switch (elementName) + { + case "plainMarker": + return PlainMarkerFromElement( + reader, out error); + default: + error = new Reporting.Error( + $"Unexpected element with the name {elementName}"); + return default!; + } + } // internal static Our.IMarker? IMarkerFromElement + + /// + /// Deserialize an instance of class PlainMarker from a sequence of XML elements. + /// + /// + /// If is set, we should try to deserialize + /// the instance from an empty sequence. That is, the parent element + /// was a self-closing element. + /// + internal static Our.PlainMarker PlainMarkerFromSequence( + Xml.XmlReader reader, + bool isEmptySequence, + out Reporting.Error? error) + { + error = null; + return new Our.PlainMarker(); + } // internal static Our.PlainMarker PlainMarkerFromSequence + /// /// Deserialize an instance of class Something from a sequence of XML elements. /// @@ -1388,6 +1439,74 @@ public static Our.Blossom BlossomFrom( return result; } + /// + /// Deserialize an instance of IMarker from . + /// + /// Initialized XML reader with cursor set to the element + /// + /// Thrown when the element is not a valid XML + /// representation of IMarker. + /// + [CodeAnalysis.SuppressMessage("ReSharper", "InconsistentNaming")]public static Our.IMarker IMarkerFrom( + Xml.XmlReader reader) + { + XmlCommon.SkipNoneWhitespaceAndComments(reader); + + if (!reader.EOF && reader.NodeType == Xml.XmlNodeType.XmlDeclaration) + { + throw new Xmlization.Exception( + "", + "Unexpected XML declaration when reading an instance " + + "of class IMarker, as we expect the reader " + + "to be set at content with MoveToContent"); + } + + Our.IMarker result = DeserializeImplementation.IMarkerFromElement( + reader, + out Reporting.Error? error); + if (error != null) + { + throw new Xmlization.Exception( + Reporting.GenerateRelativeXPath(error.PathSegments), + error.Cause); + } + return result; + } + + /// + /// Deserialize an instance of PlainMarker from . + /// + /// Initialized XML reader with cursor set to the element + /// + /// Thrown when the element is not a valid XML + /// representation of PlainMarker. + /// + public static Our.PlainMarker PlainMarkerFrom( + Xml.XmlReader reader) + { + XmlCommon.SkipNoneWhitespaceAndComments(reader); + + if (!reader.EOF && reader.NodeType == Xml.XmlNodeType.XmlDeclaration) + { + throw new Xmlization.Exception( + "", + "Unexpected XML declaration when reading an instance " + + "of class PlainMarker, as we expect the reader " + + "to be set at content with MoveToContent"); + } + + Our.PlainMarker result = DeserializeImplementation.PlainMarkerFromElement( + reader, + out Reporting.Error? error); + if (error != null) + { + throw new Xmlization.Exception( + Reporting.GenerateRelativeXPath(error.PathSegments), + error.Cause); + } + return result; + } + /// /// Deserialize an instance of Something from . /// @@ -1664,6 +1783,27 @@ public override void VisitBlossom( writer.WriteEndElement(); } + [CodeAnalysis.SuppressMessage("ReSharper", "UnusedParameter.Local")] + private static void PlainMarkerToSequence( + Our.IPlainMarker that, + Xml.XmlWriter writer) + { + // Intentionally empty. + } // private static void PlainMarkerToSequence + + public override void VisitPlainMarker( + Our.IPlainMarker that, + Xml.XmlWriter writer) + { + writer.WriteStartElement( + "plainMarker", + NS); + PlainMarkerToSequence( + that, + writer); + writer.WriteEndElement(); + } + private static void SomethingToSequence( Our.ISomething that, Xml.XmlWriter writer) diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/enhancing/enhancing.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/enhancing/enhancing.go index 272fdbd74..a2bb40c8d 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/enhancing/enhancing.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/enhancing/enhancing.go @@ -275,6 +275,59 @@ func wrapBlossom[E any]( return } +type enhancedPlainMarker[E any] struct { + instance ourtypes.IPlainMarker + enhancement E +} + +func (epm *enhancedPlainMarker[E]) ModelType( +) ourtypes.ModelType { + return epm.instance.ModelType() +} + +func (epm *enhancedPlainMarker[E]) DescendOnce( + action func(ourtypes.IClass)bool, +) bool { + return epm.instance.DescendOnce(action) +} + +func (epm *enhancedPlainMarker[E]) Descend( + action func(ourtypes.IClass) bool, +) bool { + return epm.instance.Descend(action) +} + +func (epm *enhancedPlainMarker[E]) getEnhancement( +) E { + return epm.enhancement +} + +func (epm *enhancedPlainMarker[E]) setEnhancement( + value E, +) { + epm.enhancement = value +} + +func wrapPlainMarker[E any]( + that ourtypes.IPlainMarker, + factory func(ourtypes.IClass) (E, bool), +) (result ourtypes.IPlainMarker) { + // We assume that we already checked whether `that` has been enhanced + // in the caller. + + enh, shouldEnhance := factory(that) + if shouldEnhance { + result = &enhancedPlainMarker[E]{ + instance: that, + enhancement: enh, + } + } else { + result = that + } + + return +} + type enhancedSomething[E any] struct { instance ourtypes.ISomething enhancement E @@ -488,6 +541,11 @@ func Wrap[E any]( that.(ourtypes.IBlossom), factory, ) + case ourtypes.ModelTypePlainMarker: + result = wrapPlainMarker[E]( + that.(ourtypes.IPlainMarker), + factory, + ) case ourtypes.ModelTypeSomething: result = wrapSomething[E]( that.(ourtypes.ISomething), diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/enhancing/test/enhancing_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/enhancing/test/enhancing_test.go index 9b34759a5..74965da0f 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/enhancing/test/enhancing_test.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/enhancing/test/enhancing_test.go @@ -230,6 +230,67 @@ func TestBlossomNothingWrapped(t *testing.T) { }) } +func TestPlainMarkerWrapped(t *testing.T) { + instance := ourtesting.MustLoadMaximalPlainMarker() + + nextID := 0 + wrapped := ourenhancing.Wrap[*Enhancement]( + instance, + func(that ourtypes.IClass) (enh *Enhancement, should bool) { + enh = &Enhancement{} + enh.ID = nextID + should = true + + nextID++ + return + }, + ) + + if !ourtesting.DeepEqual(instance, wrapped) { + t.Fatalf( + "Deep equality failed between the instance and the wrapped: %v %v", + instance, wrapped, + ) + } + + collectIDsAndAssertTheyAreConsecutiveAndTheirCountEqualsNextID( + t, wrapped, nextID, + ) +} + +func TestPlainMarkerNothingWrapped(t *testing.T) { + instance := ourtesting.MustLoadMaximalPlainMarker() + + wrapped := ourenhancing.Wrap[*Enhancement]( + instance, + func(that ourtypes.IClass) (enh *Enhancement, should bool) { + should = false + return + }, + ) + + if !ourtesting.DeepEqual(instance, wrapped) { + t.Fatalf( + "Deep equality failed between the instance and the wrapped: %v %v", + instance, wrapped, + ) + } + + // Wrapped should be equal to instance by reference as our enhancement factory + // did not wrap anything. + if wrapped != instance { + t.Fatalf("Unexpected inequality between %v and %v", wrapped, instance) + } + + wrapped.Descend(func (that ourtypes.IClass) (abort bool) { + _, ok := ourenhancing.Unwrap[*Enhancement](that) + if ok { + t.Fatalf("Unexpected wrapped descendant: %v", that) + } + return + }) +} + func TestSomethingWrapped(t *testing.T) { instance := ourtesting.MustLoadMaximalSomething() diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/jsonization.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/jsonization.go index 94fcdea0c..901888611 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/jsonization.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/jsonization.go @@ -719,6 +719,74 @@ func blossomFromMapWithoutDispatch( return } +// Parse `jsonable` as an instance of [ourtypes.IMarker], +// or return an error. +func MarkerFromJsonable( + jsonable interface{}, +) ( + result ourtypes.IMarker, + err error, +) { + m, ok := jsonable.(map[string]interface{}) + if !ok { + err = notAMapError(jsonable) + return + } + + return markerFromMap(m) +} + +// Parse `jsonable` as an instance of [ourtypes.IPlainMarker], +// or return an error. +func PlainMarkerFromJsonable( + jsonable interface{}, +) ( + result ourtypes.IPlainMarker, + err error, +) { + m, ok := jsonable.(map[string]interface{}) + if !ok { + err = notAMapError(jsonable) + return + } + + err = checkModelType(m, "PlainMarker") + if err != nil { + return + } + + return plainMarkerFromMapWithoutDispatch(m) +} + +// Parse [ourtypes.IPlainMarker] from a map, +// or return an error, if any. +func plainMarkerFromMapWithoutDispatch( + m map[string]interface{}, +) ( + result ourtypes.IPlainMarker, + err error, +) { + for k := range m { + switch k { + case "modelType": + // The model type has already been checked before the loop. + + default: + err = newDeserializationError( + fmt.Sprintf( + "Unexpected property: %s", + k, + ), + ) + return + } + } + + result = ourtypes.NewPlainMarker() + + return +} + // Parse `jsonable` as an instance of [ourtypes.ISomething], // or return an error. func SomethingFromJsonable( @@ -977,6 +1045,36 @@ func leafFromMap( return } +// De-serialize an instance of [ourtypes.IMarker] +// from a map by dispatching to the concrete `*FromMapWithoutDispatch` function. +func markerFromMap( + m map[string]interface{}, +) ( + result ourtypes.IMarker, + err error, +) { + var modelType string + modelType, err = modelTypeFromMap(m) + if err != nil { + return + } + + switch modelType { + case "PlainMarker": + result, err = plainMarkerFromMapWithoutDispatch(m) + default: + err = newDeserializationError( + fmt.Sprintf( + "Unexpected model type " + + "for IMarker: %s", + modelType, + ), + ) + } + + return +} + // endregion // region Serialization @@ -1233,6 +1331,22 @@ func blossomToMap( return } +// Serialize [ourtypes.IPlainMarker] as a JSON-able map. +// +// This function performs no dispatch! It is only used to serialize +// the properties. If you want to serialize an instance of +// [ourtypes.IPlainMarker] with proper dispatch, call +// [ToJsonable]. +func plainMarkerToMap( + that ourtypes.IPlainMarker, +) (result map[string]interface{}, err error) { + result = make(map[string]interface{}) + + result["modelType"] = "PlainMarker" + + return +} + // Serialize [ourtypes.ISomething] as a JSON-able map. // // This function performs no dispatch! It is only used to serialize @@ -1305,6 +1419,10 @@ func ToJsonable( result, err = blossomToMap( that.(ourtypes.IBlossom), ) + case ourtypes.ModelTypePlainMarker: + result, err = plainMarkerToMap( + that.(ourtypes.IPlainMarker), + ) case ourtypes.ModelTypeSomething: result, err = somethingToMap( that.(ourtypes.ISomething), diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/test/classes_with_descendants_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/test/classes_with_descendants_test.go index 7921a2de1..88ff74666 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/test/classes_with_descendants_test.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/test/classes_with_descendants_test.go @@ -165,5 +165,83 @@ func TestLeafRoundTripOKOverDescendant(t *testing.T) { } } +func TestMarkerRoundTripOKOverDescendant(t *testing.T) { + instance := ourtesting.MustLoadMinimalPlainMarker() + + jsonable, err := ourjsonization.ToJsonable(instance) + if err != nil { + t.Fatalf( + "Failed to serialize the minimal PlainMarker: %v", + err, + ) + return + } + + source := "" + + deserialized, deseriaErr := ourjsonization.MarkerFromJsonable( + jsonable, + ) + ok := assertNoDeserializationError(t, deseriaErr, source) + if !ok { + return + } + + anotherJsonable, seriaErr := ourjsonization.ToJsonable(deserialized) + ok = assertNoSerializationError(t, seriaErr, source) + if !ok { + return + } + + ok = assertSerializationEqualsDeserialization( + t, + jsonable, + anotherJsonable, + source, + ) + if !ok { + return + } +} + +func TestMarkerDeserializationFail(t *testing.T) { + jsonable := any("this is not an object") + + _, err := ourjsonization.MarkerFromJsonable( + jsonable, + ) + + if err == nil { + t.Fatal("Expected an error, but got none.") + return + } + + deseriaErr, ok := err.(*ourjsonization.DeserializationError) + if !ok { + t.Fatalf("Expected a de-serialization error, but got: %v", err) + return + } + + pathString := deseriaErr.PathString() + if len(pathString) != 0 { + t.Fatalf( + "Expected an empty path in error, but got: %s", + pathString, + ) + return + } + + expectedMessage := + "Expected a JSON object, but got string" + + if deseriaErr.Message != expectedMessage { + t.Fatalf( + "Expected the deserialization error:\n%s\n, but got:\n%s", + expectedMessage, deseriaErr.Message, + ) + return + } +} + // This code has been automatically generated by aas-core-codegen. // Do NOT edit or append. diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/test/concrete_classes_test.go index 7bac5ef86..c0b3861aa 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/test/concrete_classes_test.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/jsonization/test/concrete_classes_test.go @@ -386,6 +386,111 @@ func TestBlossomDeserializationFail(t *testing.T) { } } +func TestPlainMarkerRoundTripOK(t *testing.T) { + pths := ourtesting.FindFilesBySuffixRecursively( + filepath.Join( + ourtesting.TestDataDir, + "Json", + "Expected", + "PlainMarker", + ), + ".json", + ) + sort.Strings(pths) + + for _, pth := range pths { + jsonable := ourtesting.MustReadJsonable( + pth, + ) + + deserialized, deseriaErr := ourjsonization.PlainMarkerFromJsonable( + jsonable, + ) + ok := assertNoDeserializationError(t, deseriaErr, pth) + if !ok { + return + } + + anotherJsonable, seriaErr := ourjsonization.ToJsonable(deserialized) + ok = assertNoSerializationError(t, seriaErr, pth) + if !ok { + return + } + + ok = assertSerializationEqualsDeserialization( + t, + jsonable, + anotherJsonable, + pth, + ) + if !ok { + return + } + } +} + +func TestPlainMarkerDeserializationFail(t *testing.T) { + pattern := filepath.Join( + ourtesting.TestDataDir, + "Json", + "Unexpected", + "Unserializable", + "*", // This asterisk represents the cause. + "PlainMarker", + ) + + causeDirs, err := filepath.Glob(pattern) + if err != nil { + panic( + fmt.Sprintf( + "Failed to find cause directories matching %s: %s", + pattern, err.Error(), + ), + ) + } + + for _, causeDir := range causeDirs { + pths := ourtesting.FindFilesBySuffixRecursively( + causeDir, + ".json", + ) + sort.Strings(pths) + + for _, pth := range pths { + jsonable := ourtesting.MustReadJsonable( + pth, + ) + + relPth, err := filepath.Rel(ourtesting.TestDataDir, pth) + if err != nil { + panic( + fmt.Sprintf( + "Failed to compute the relative path of %s to %s: %s", + ourtesting.TestDataDir, pth, err.Error(), + ), + ) + } + + expectedPth := filepath.Join( + ourtesting.TestDataDir, + "DeserializationError", + filepath.Dir(relPth), + filepath.Base(relPth)+".error", + ) + + _, deseriaErr := ourjsonization.PlainMarkerFromJsonable( + jsonable, + ) + ok := assertDeserializationErrorEqualsExpectedOrRecord( + t, deseriaErr, pth, expectedPth, + ) + if !ok { + return + } + } + } +} + func TestSomethingRoundTripOK(t *testing.T) { pths := ourtesting.FindFilesBySuffixRecursively( filepath.Join( diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/ourtesting/common_jsonization.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/ourtesting/common_jsonization.go index 5c727fcfb..d3d378ae3 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/ourtesting/common_jsonization.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/ourtesting/common_jsonization.go @@ -262,6 +262,90 @@ func MustLoadMinimalBlossom( return } +// Load a maximal example of [ourtypes.IPlainMarker] from +// the test data directory. +// +// If there is any error, panic. +func MustLoadMaximalPlainMarker( +) (result ourtypes.IPlainMarker) { + pth := path.Join( + TestDataDir, + "Json", + "Expected", + "PlainMarker", + "maximal.json", + ) + + jsonable := MustReadJsonable(pth) + + instance, err := ourjsonization.PlainMarkerFromJsonable( + jsonable, + ) + if err != nil { + panic( + fmt.Sprintf( + "Failed to de-serialize an instance of IPlainMarker " + + "from %s: %s", + pth, err.Error(), + ), + ) + } + var ok bool + result, ok = instance.(ourtypes.IPlainMarker) + if !ok { + panic( + fmt.Sprintf( + "Expected to find an instance of IPlainMarker at %s, " + + "but got an instance of %T: %v", + pth, instance, instance, + ), + ) + } + return +} + +// Load a minimal example of [ourtypes.IPlainMarker] from +// the test data directory. +// +// If there is any error, panic. +func MustLoadMinimalPlainMarker( +) (result ourtypes.IPlainMarker) { + pth := path.Join( + TestDataDir, + "Json", + "Expected", + "PlainMarker", + "minimal.json", + ) + + jsonable := MustReadJsonable(pth) + + instance, err := ourjsonization.PlainMarkerFromJsonable( + jsonable, + ) + if err != nil { + panic( + fmt.Sprintf( + "Failed to de-serialize an instance of IPlainMarker " + + "from %s: %s", + pth, err.Error(), + ), + ) + } + var ok bool + result, ok = instance.(ourtypes.IPlainMarker) + if !ok { + panic( + fmt.Sprintf( + "Expected to find an instance of IPlainMarker at %s, " + + "but got an instance of %T: %v", + pth, instance, instance, + ), + ) + } + return +} + // Load a maximal example of [ourtypes.ISomething] from // the test data directory. // diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/ourtesting/deep_equal.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/ourtesting/deep_equal.go index bd7454cd8..0a068f11a 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/ourtesting/deep_equal.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/ourtesting/deep_equal.go @@ -92,6 +92,16 @@ func deepEqualBlossom( return true } +// Perform a comparison for deep equality between `that` and `other` instance. +// +// The deep equality means that all the properties are checked for equality recursively. +func deepEqualPlainMarker( + that ourtypes.IPlainMarker, + other ourtypes.IPlainMarker, +) bool { + return true +} + // Perform a comparison for deep equality between `that` and `other` instance. // // The deep equality means that all the properties are checked for equality recursively. @@ -172,6 +182,11 @@ func DeepEqual( that.(ourtypes.IBlossom), other.(ourtypes.IBlossom), ) + case ourtypes.ModelTypePlainMarker: + return deepEqualPlainMarker( + that.(ourtypes.IPlainMarker), + other.(ourtypes.IPlainMarker), + ) case ourtypes.ModelTypeSomething: return deepEqualSomething( that.(ourtypes.ISomething), diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/stringification/stringification.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/stringification/stringification.go index 58f7ad136..2030f9ba0 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/stringification/stringification.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/stringification/stringification.go @@ -13,6 +13,7 @@ var modelTypeFromStringMap = map[string]ourtypes.ModelType { "Branch": ourtypes.ModelTypeBranch, "Leaf": ourtypes.ModelTypeLeaf, "Blossom": ourtypes.ModelTypeBlossom, + "PlainMarker": ourtypes.ModelTypePlainMarker, "Something": ourtypes.ModelTypeSomething, "Container": ourtypes.ModelTypeContainer, } @@ -31,6 +32,7 @@ var modelTypeToStringArray = [...]string { "Branch", "Leaf", "Blossom", + "PlainMarker", "Something", "Container", } diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/descend_test/descend_once_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/descend_test/descend_once_test.go index 3844fb3b7..a75417621 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/descend_test/descend_once_test.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/descend_test/descend_once_test.go @@ -81,6 +81,30 @@ func TestDescendOnceOnAnInstanceOfBlossom( } } +func TestDescendOnceOnAnInstanceOfPlainMarker( + t *testing.T, +) { + instance := ourtesting.MustLoadMaximalPlainMarker() + + expectedPth := filepath.Join( + ourtesting.TestDataDir, + "DescendOnce", + "PlainMarker", + "maximal.json.trace", + ) + + onlyOnce := true + + message := compareOrRerecordTrace( + instance, + expectedPth, + onlyOnce, + ) + if message != nil { + t.Fatal(*message) + } +} + func TestDescendOnceOnAnInstanceOfSomething( t *testing.T, ) { diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/descend_test/descend_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/descend_test/descend_test.go index 76b90c38c..10c2f44d0 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/descend_test/descend_test.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/descend_test/descend_test.go @@ -81,6 +81,30 @@ func TestDescendOnAnInstanceOfBlossom( } } +func TestDescendOnAnInstanceOfPlainMarker( + t *testing.T, +) { + instance := ourtesting.MustLoadMaximalPlainMarker() + + expectedPth := filepath.Join( + ourtesting.TestDataDir, + "Descend", + "PlainMarker", + "maximal.json.trace", + ) + + onlyOnce := false + + message := compareOrRerecordTrace( + instance, + expectedPth, + onlyOnce, + ) + if message != nil { + t.Fatal(*message) + } +} + func TestDescendOnAnInstanceOfSomething( t *testing.T, ) { diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/is_xxx_test/is_xxx_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/is_xxx_test/is_xxx_test.go index 5a472829d..5378f033d 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/is_xxx_test/is_xxx_test.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/is_xxx_test/is_xxx_test.go @@ -36,6 +36,14 @@ func TestIsXxxOnAnInstanceOfBranch(t *testing.T) { ) } + if ourtypes.IsPlainMarker(instance) { + t.Errorf( + "Expected IsPlainMarker to be false on an instance " + + "of IBranch with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + if ourtypes.IsSomething(instance) { t.Errorf( "Expected IsSomething to be false on an instance " + @@ -80,6 +88,14 @@ func TestIsXxxOnAnInstanceOfLeaf(t *testing.T) { ) } + if ourtypes.IsPlainMarker(instance) { + t.Errorf( + "Expected IsPlainMarker to be false on an instance " + + "of ILeaf with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + if ourtypes.IsSomething(instance) { t.Errorf( "Expected IsSomething to be false on an instance " + @@ -124,6 +140,14 @@ func TestIsXxxOnAnInstanceOfBlossom(t *testing.T) { ) } + if ourtypes.IsPlainMarker(instance) { + t.Errorf( + "Expected IsPlainMarker to be false on an instance " + + "of IBlossom with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + if ourtypes.IsSomething(instance) { t.Errorf( "Expected IsSomething to be false on an instance " + @@ -141,6 +165,58 @@ func TestIsXxxOnAnInstanceOfBlossom(t *testing.T) { } } +func TestIsXxxOnAnInstanceOfPlainMarker(t *testing.T) { + instance := ourtesting.MustLoadMinimalPlainMarker() + + if ourtypes.IsBranch(instance) { + t.Errorf( + "Expected IsBranch to be false on an instance " + + "of IPlainMarker with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + + if ourtypes.IsLeaf(instance) { + t.Errorf( + "Expected IsLeaf to be false on an instance " + + "of IPlainMarker with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + + if ourtypes.IsBlossom(instance) { + t.Errorf( + "Expected IsBlossom to be false on an instance " + + "of IPlainMarker with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + + if !ourtypes.IsPlainMarker(instance) { + t.Errorf( + "Expected IsPlainMarker to be true on an instance " + + "of IPlainMarker with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + + if ourtypes.IsSomething(instance) { + t.Errorf( + "Expected IsSomething to be false on an instance " + + "of IPlainMarker with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + + if ourtypes.IsContainer(instance) { + t.Errorf( + "Expected IsContainer to be false on an instance " + + "of IPlainMarker with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } +} + func TestIsXxxOnAnInstanceOfSomething(t *testing.T) { instance := ourtesting.MustLoadMinimalSomething() @@ -168,6 +244,14 @@ func TestIsXxxOnAnInstanceOfSomething(t *testing.T) { ) } + if ourtypes.IsPlainMarker(instance) { + t.Errorf( + "Expected IsPlainMarker to be false on an instance " + + "of ISomething with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + if !ourtypes.IsSomething(instance) { t.Errorf( "Expected IsSomething to be true on an instance " + @@ -212,6 +296,14 @@ func TestIsXxxOnAnInstanceOfContainer(t *testing.T) { ) } + if ourtypes.IsPlainMarker(instance) { + t.Errorf( + "Expected IsPlainMarker to be false on an instance " + + "of IContainer with runtime type %T and with model type %v", + instance, instance.ModelType(), + ) + } + if ourtypes.IsSomething(instance) { t.Errorf( "Expected IsSomething to be false on an instance " + diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/types.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/types.go index 85b167e92..8444c2fed 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/types.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/types/types.go @@ -13,6 +13,7 @@ const ( ModelTypeBranch ModelType = iota ModelTypeLeaf ModelTypeBlossom + ModelTypePlainMarker ModelTypeSomething ModelTypeContainer ) @@ -458,6 +459,101 @@ func NewBlossom( } } +type IMarker interface { + IClass +} + +// Check whether the instance corresponds to [ourtypes.IMarker] +// based on its run-time model type. +// +// The implementation uses a switch statements which is +// most probably compiled as an efficient jump table by the compiler. +func IsMarker( + that IClass, +) (ok bool) { + switch that.ModelType() { + case ModelTypePlainMarker: + ok = true + } + return +} + +type IPlainMarker interface { + IMarker +} + +// Check whether the instance corresponds to [ourtypes.IPlainMarker] +// based on its run-time model type. +// +// The implementation uses a switch statements which is +// most probably compiled as an efficient jump table by the compiler. +func IsPlainMarker( + that IClass, +) (ok bool) { + ok = that.ModelType() == ModelTypePlainMarker + return +} + +// Implements IPlainMarker. +type PlainMarker struct { + +} + +func (pm *PlainMarker) ModelType( +) ModelType { + return ModelTypePlainMarker +} + +// Apply the action on the instances referenced from pm. +// +// If any of the actions returns abort `true`, the descent is immediately +// stopped, and abort `true` is also returned. Otherwise, return abort `false`. +// +// We do not recurse into the referenced instances. +// +// The action is not applied on pm. +func (pm *PlainMarker) DescendOnce( + action func(IClass) bool, +) (abort bool) { + return descend_PlainMarker(pm, action, false) +} + +// Apply the action recursively on the instances referenced from pm. +// +// If any of the actions returns abort `true`, the descent is immediately +// stopped, and abort `true` is also returned. Otherwise, return abort `false`. +// +// The action is not applied on pm. +func (pm *PlainMarker) Descend( + action func(IClass) bool, +) (abort bool) { + return descend_PlainMarker(pm, action, true) +} + +// Apply the action on the instances referenced from that, and recursively +// on their descendants if recurse is set. +// +// If any of the actions returns abort `true`, the descent is immediately +// stopped, and abort `true` is also returned. Otherwise, return abort `false`. +// +// The action is not applied on that. +func descend_PlainMarker( + that *PlainMarker, + action func(IClass) bool, + recurse bool, +) (abort bool) { + // No descendable properties + + return +} + +// Create a new instance of PlainMarker with +// the given properties. +func NewPlainMarker() *PlainMarker { + // Intentionally empty. + return &PlainMarker{} +} + type ISomething interface { IClass diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/verification/test/verification_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/verification/test/verification_test.go index 54875c7e4..3dd000104 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/verification/test/verification_test.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/verification/test/verification_test.go @@ -558,6 +558,136 @@ func TestBlossomFail(t *testing.T) { } } +func TestPlainMarkerOK(t *testing.T) { + pths := ourtesting.FindFilesBySuffixRecursively( + filepath.Join( + ourtesting.TestDataDir, + "Json", + "Expected", + "PlainMarker", + ), + ".json", + ) + sort.Strings(pths) + + for _, pth := range pths { + jsonable := ourtesting.MustReadJsonable( + pth, + ) + + deserialized, deseriaErr := ourjsonization.PlainMarkerFromJsonable( + jsonable, + ) + if deseriaErr != nil { + t.Fatalf( + "Unexpected deserialization error from %s: %s", + pth, deseriaErr.Error(), + ) + return + } + + var errors []*ourverification.VerificationError + ourverification.Verify( + deserialized, + func(veriErr *ourverification.VerificationError) (abort bool) { + errors = append(errors, veriErr) + return + }, + ) + + ok := assertNoVerificationErrors( + t, + deserialized, + pth, + ) + if !ok { + return + } + } +} + +func TestPlainMarkerFail(t *testing.T) { + pattern := filepath.Join( + ourtesting.TestDataDir, + "Json", + "Unexpected", + "Invalid", + "*", // This asterisk represents the cause. + "PlainMarker", + ) + + causeDirs, err := filepath.Glob(pattern) + if err != nil { + panic( + fmt.Sprintf( + "Failed to find cause directories matching %s: %s", + pattern, err.Error(), + ), + ) + } + + for _, causeDir := range causeDirs { + pths := ourtesting.FindFilesBySuffixRecursively( + causeDir, + ".json", + ) + sort.Strings(pths) + + for _, pth := range pths { + jsonable := ourtesting.MustReadJsonable( + pth, + ) + + relPth, err := filepath.Rel(ourtesting.TestDataDir, pth) + if err != nil { + panic( + fmt.Sprintf( + "Failed to compute the relative path of %s to %s: %s", + ourtesting.TestDataDir, pth, err.Error(), + ), + ) + } + + expectedPth := filepath.Join( + ourtesting.TestDataDir, + "VerificationError", + filepath.Dir(relPth), + filepath.Base(relPth)+".errors", + ) + + deserialized, deseriaErr := ourjsonization.PlainMarkerFromJsonable( + jsonable, + ) + if deseriaErr != nil { + t.Fatalf( + "Unexpected deserialization error from %s: %s", + pth, deseriaErr.Error(), + ) + return + } + + var errors []*ourverification.VerificationError + ourverification.Verify( + deserialized, + func(err *ourverification.VerificationError) (abort bool) { + errors = append(errors, err) + return + }, + ) + + ok := assertEqualsExpectedOrRerecordVerificationErrors( + t, + errors, + pth, + expectedPth, + ) + if !ok { + return + } + } + } +} + func TestSomethingOK(t *testing.T) { pths := ourtesting.FindFilesBySuffixRecursively( filepath.Join( diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/verification/verification.go index 057fb38f4..3311341b0 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/verification/verification.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/verification/verification.go @@ -95,6 +95,23 @@ func VerifyBlossom( return } +// Verify `that` instance of [ourtypes.IPlainMarker]. +// +// You have to supply the callback `onError` to iterate over the errors. +// If `onError` returns abort `true`, this function will abort +// further verification as well, and return abort `true`. Otherwise, +// abort `false` is returned. +func VerifyPlainMarker( + that ourtypes.IPlainMarker, + onError func(*VerificationError) bool, +) (abort bool) { + abort = false + + // No verification has been defined for IPlainMarker. + + return +} + // Verify `that` instance of [ourtypes.ISomething]. // // You have to supply the callback `onError` to iterate over the errors. @@ -256,6 +273,11 @@ func Verify( that.(ourtypes.IBlossom), onError, ) + case ourtypes.ModelTypePlainMarker: + abort = VerifyPlainMarker( + that.(ourtypes.IPlainMarker), + onError, + ) case ourtypes.ModelTypeSomething: abort = VerifySomething( that.(ourtypes.ISomething), diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/concrete_classes_test.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/concrete_classes_test.go index effbb84b3..f884d5a54 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/concrete_classes_test.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/test/concrete_classes_test.go @@ -395,6 +395,132 @@ func TestBlossomDeserializationFail(t *testing.T) { } } +func TestPlainMarkerRoundTripOK(t *testing.T) { + pths := ourtesting.FindFilesBySuffixRecursively( + filepath.Join( + ourtesting.TestDataDir, + "Xml", + "Expected", + "plainMarker", + ), + ".xml", + ) + sort.Strings(pths) + + for _, pth := range pths { + bb, err := os.ReadFile(pth) + if err != nil { + t.Fatalf("Failed to read the file %s: %s", pth, err.Error()) + return + } + text := string(bb) + + decoder := xml.NewDecoder(strings.NewReader(text)) + + deserialized, deseriaErr := ourxmlization.Unmarshal(decoder) + ok := assertNoDeserializationError(t, deseriaErr, pth) + if !ok { + return + } + + if _, ok := deserialized.(ourtypes.IPlainMarker); !ok { + t.Fatalf( + "Expected an instance of IPlainMarker, "+ + "but got %T: %v", + deserialized, deserialized, + ) + return + } + + buf := &bytes.Buffer{} + encoder := xml.NewEncoder(buf) + encoder.Indent("", "\t") + + seriaErr := ourxmlization.Marshal(encoder, deserialized, true) + ok = assertNoSerializationError(t, seriaErr, pth) + if !ok { + return + } + + roundTrip := string(buf.Bytes()) + + ok = assertSerializationEqualsDeserialization( + t, + text, + roundTrip, + pth, + ) + if !ok { + return + } + } +} + +func TestPlainMarkerDeserializationFail(t *testing.T) { + pattern := filepath.Join( + ourtesting.TestDataDir, + "Xml", + "Unexpected", + "Unserializable", + "*", // This asterisk represents the cause. + "plainMarker", + ) + + causeDirs, err := filepath.Glob(pattern) + if err != nil { + panic( + fmt.Sprintf( + "Failed to find cause directories matching %s: %s", + pattern, err.Error(), + ), + ) + } + + for _, causeDir := range causeDirs { + pths := ourtesting.FindFilesBySuffixRecursively( + causeDir, + ".xml", + ) + sort.Strings(pths) + + for _, pth := range pths { + relPth, err := filepath.Rel(ourtesting.TestDataDir, pth) + if err != nil { + panic( + fmt.Sprintf( + "Failed to compute the relative path of %s to %s: %s", + ourtesting.TestDataDir, pth, err.Error(), + ), + ) + } + + expectedPth := filepath.Join( + ourtesting.TestDataDir, + "DeserializationError", + filepath.Dir(relPth), + filepath.Base(relPth)+".error", + ) + + bb, err := os.ReadFile(pth) + if err != nil { + t.Fatalf("Failed to read the file %s: %s", pth, err.Error()) + return + } + text := string(bb) + + decoder := xml.NewDecoder(strings.NewReader(text)) + + _, deseriaErr := ourxmlization.Unmarshal(decoder) + ok := assertIsDeserializationErrorAndEqualsExpectedOrRecord( + t, deseriaErr, pth, expectedPth, + ) + if !ok { + return + } + } + } +} + func TestSomethingRoundTripOK(t *testing.T) { pths := ourtesting.FindFilesBySuffixRecursively( filepath.Join( diff --git a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/xmlization.go b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/xmlization.go index ca1fe14f3..ba3765b36 100644 --- a/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/xmlization.go +++ b/dev/test_data/main/golang/expected/deep_class_hierarchy/expected_output/xmlization/xmlization.go @@ -606,6 +606,53 @@ func readBlossomAsSequence( return } +// De-serialize the instance of [ourtypes.IPlainMarker] +// as a sequence of XML elements, each representing a property +// of [ourtypes.IPlainMarker]. +// +// The reading stops as soon as we encounter a non-start element, and we return +// that token as the `next` token. +func readPlainMarkerAsSequence( + decoder *xml.Decoder, + current xml.Token, +) (instance ourtypes.IPlainMarker, + next xml.Token, + err error, +) { + // No initialization as there are no properties + // in IPlainMarker. + + for { + var local string + var ok bool + local, current, ok, err = nextProperty(decoder, current, "IPlainMarker") + if err != nil { + return + } + if !ok { + break + } + + var valueErr error + switch local { + default: + valueErr = xmlcommon.NewDeserializationError( + "Unexpected property", + ) + } + + current, err = concludeProperty(decoder, current, local, valueErr) + if err != nil { + return + } + } + + next = current + + instance = ourtypes.NewPlainMarker() + return +} + // De-serialize the instance of [ourtypes.ISomething] // as a sequence of XML elements, each representing a property // of [ourtypes.ISomething]. @@ -788,6 +835,8 @@ func readClassDispatched( instance, next, err = readLeafAsSequence(decoder, current) case "blossom": instance, next, err = readBlossomAsSequence(decoder, current) + case "plainMarker": + instance, next, err = readPlainMarkerAsSequence(decoder, current) case "something": instance, next, err = readSomethingAsSequence(decoder, current) case "container": @@ -1152,6 +1201,23 @@ func writeBlossomAsSequence( return } +// Serialize the instance +// of [ourtypes.IPlainMarker] +// as a sequence of properties, each represented as an XML element. +// +// The XML namespace is expected to be set in the one of the parent elements +// enclosing the sequence. +// +// Do not flush. +func writePlainMarkerAsSequence( + encoder *xml.Encoder, + that ourtypes.IPlainMarker, +) (err error) { + // Intentionally empty. + + return +} + // Serialize the instance // of [ourtypes.ISomething] // as a sequence of properties, each represented as an XML element. @@ -1257,6 +1323,14 @@ func writeClass( that.(ourtypes.IBlossom), writeBlossomAsSequence, ) + case ourtypes.ModelTypePlainMarker: + err = writeClassElement( + encoder, + "plainMarker", + withNamespace, + that.(ourtypes.IPlainMarker), + writePlainMarkerAsSequence, + ) case ourtypes.ModelTypeSomething: err = writeClassElement( encoder, diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/copying/Copying.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/copying/Copying.java index eb47a19bc..63e6a99b1 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/copying/Copying.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/copying/Copying.java @@ -79,6 +79,13 @@ public IClass transformBlossom( that.getDetails()); } + @Override + public IClass transformPlainMarker( + IPlainMarker that + ) { + return new PlainMarker(); + } + @Override public IClass transformSomething( ISomething that @@ -131,6 +138,13 @@ public IClass transformBlossom ( ); } + @Override + public IClass transformPlainMarker ( + IPlainMarker that + ) { + return new PlainMarker(); + } + @Override public IClass transformSomething ( ISomething that diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/enhancing/EnhancedPlainMarker.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/enhancing/EnhancedPlainMarker.java new file mode 100644 index 000000000..c3f2af2a8 --- /dev/null +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/enhancing/EnhancedPlainMarker.java @@ -0,0 +1,67 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.enhancing; + +import java.lang.Iterable; +import java.util.Optional; +import java.util.List; +import dummy.common.*; +import dummy.visitation.IVisitor; +import dummy.visitation.IVisitorWithContext; +import dummy.visitation.ITransformer; +import dummy.visitation.ITransformerWithContext; +import dummy.types.enums.*; +import dummy.types.impl.*; +import dummy.types.model.*; + +public class EnhancedPlainMarker + extends Enhanced + implements IPlainMarker { + private final IPlainMarker instance; + + public EnhancedPlainMarker( + IPlainMarker instance, + EnhancementT enhancement + ) { + super(enhancement); + this.instance = instance; + } + + public Iterable descendOnce() { + return instance.descendOnce(); + } + + public Iterable descend() { + return instance.descend(); + } + + public void accept(IVisitor visitor) { + visitor.visitPlainMarker(instance); + } + + public void accept( + IVisitorWithContext visitor, + ContextT context + ) { + visitor.visitPlainMarker(instance, context); + } + + public T transform(ITransformer transformer) { + return transformer.transformPlainMarker(instance); + } + + public T transform( + ITransformerWithContext transformer, + ContextT context + ) { + return transformer.transformPlainMarker(instance, context); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/enhancing/_Wrapper.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/enhancing/_Wrapper.java index 4be30ad9d..526c2a6a4 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/enhancing/_Wrapper.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/enhancing/_Wrapper.java @@ -83,6 +83,26 @@ public IClass transformBlossom( ); } + @Override + public IClass transformPlainMarker( + IPlainMarker that + ) { + if (that instanceof Enhanced) + { + throw new IllegalArgumentException( + "The instance has been already enhanced: " + that + ); + } + + Optional enhancement = enhancementFactory.apply(that); + return !enhancement.isPresent() + ? that + : new EnhancedPlainMarker<>( + that, + enhancement.get() + ); + } + @Override public IClass transformSomething( ISomething that diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/jsonization/Jsonization.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/jsonization/Jsonization.java index ca8a33453..d14af9f5b 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/jsonization/Jsonization.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/jsonization/Jsonization.java @@ -578,6 +578,74 @@ private static Reporting.Result tryBlossomFromObject(JsonNode node) { theDetails)); } + /** + * Deserialize an instance of IMarker by dispatching + * based on {@code modelType} property of the {@code node}. + * + * @param node JSON node to be parsed + */ + public static Reporting.Result tryIMarkerFrom(JsonNode node) { + if (node == null || !node.isObject()) { + return notAJsonObject(node); + } + + final Reporting.Result modelTypeResult = tryModelTypeFrom(node); + if (modelTypeResult.isError()) { + return modelTypeResult.castTo(IMarker.class); + } + + switch (modelTypeResult.getResult()) { + case "PlainMarker": + return tryPlainMarkerFromObject(node); + default: { + final Reporting.Error error = new Reporting.Error( + "Unexpected model type for IMarker: " + modelTypeResult.getResult()); + return Reporting.Result.failure(error); + } + } + } + + /** + * Deserialize an instance of PlainMarker from {@code node}. + * + * @param node JSON node to be parsed + */ + private static Reporting.Result tryPlainMarkerFrom(JsonNode node) { + if (node == null || !node.isObject()) { + return notAJsonObject(node); + } + + final Reporting.Error modelTypeError = checkModelType(node, "PlainMarker"); + if (modelTypeError != null) { + return Reporting.Result.failure(modelTypeError); + } + + return tryPlainMarkerFromObject(node); + } + + /** + * Deserialize an instance of PlainMarker from the JSON object {@code node} whose + * model type has already been checked. + * + * @param node JSON object to be parsed + */ + private static Reporting.Result tryPlainMarkerFromObject(JsonNode node) { + for (Iterator> iterator = node.fields(); iterator.hasNext(); ) { + final Map.Entry keyValue = iterator.next(); + final String key = keyValue.getKey(); + + switch (key) { + case "modelType": + // The model type has already been checked before the loop. + break; + default: + return unexpectedProperty(key); + } + } + + return Reporting.Result.success(new PlainMarker()); + } + /** * Deserialize an instance of Something from {@code node}. * @@ -871,6 +939,40 @@ public static Blossom deserializeBlossom(JsonNode node) { }); } + /** + * Deserialize an instance of IMarker from {@code node}. + * + * @param node JSON node to be parsed + */ + public static IMarker deserializeIMarker(JsonNode node) { + final Reporting.Result result = + _DeserializeImplementation.tryIMarkerFrom( + node); + + return result.onError(error -> { + throw new DeserializeException( + Reporting.generateJsonPath(error.getPathSegments()), + error.getCause()); + }); + } + + /** + * Deserialize an instance of PlainMarker from {@code node}. + * + * @param node JSON node to be parsed + */ + public static PlainMarker deserializePlainMarker(JsonNode node) { + final Reporting.Result result = + _DeserializeImplementation.tryPlainMarkerFrom( + node); + + return result.onError(error -> { + throw new DeserializeException( + Reporting.generateJsonPath(error.getPathSegments()), + error.getCause()); + }); + } + /** * Deserialize an instance of Something from {@code node}. * @@ -1081,6 +1183,17 @@ public JsonNode transformBlossom( return result; } + @Override + public JsonNode transformPlainMarker( + IPlainMarker that + ) { + final ObjectNode result = JsonNodeFactory.instance.objectNode(); + + result.put("modelType", "PlainMarker"); + + return result; + } + @Override public JsonNode transformSomething( ISomething that diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/impl/PlainMarker.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/impl/PlainMarker.java new file mode 100644 index 000000000..7dff8ffdf --- /dev/null +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/impl/PlainMarker.java @@ -0,0 +1,79 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.types.impl; + +import dummy.common.*; +import dummy.visitation.IVisitor; +import dummy.visitation.IVisitorWithContext; +import dummy.visitation.ITransformer; +import dummy.visitation.ITransformerWithContext; +import dummy.types.enums.*; +import dummy.types.impl.*; +import dummy.types.model.*; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Objects; +import dummy.types.model.IPlainMarker; + +public class PlainMarker implements IPlainMarker { + /** + * Iterate recursively over all the class instances referenced from this instance. + */ + public Iterable descend() { + return Collections.emptyList(); + } + + /** + * Iterate over all the class instances referenced from this instance. + */ + public Iterable descendOnce() { + return Collections.emptyList(); + } + + /** + * Accept the {@code visitor} to visit this instance for double dispatch. + **/ + @Override + public void accept(IVisitor visitor) { + visitor.visitPlainMarker(this); + } + + /** + * Accept the {@code visitor} to visit this instance for double dispatch + * with the {@code context}. + **/ + @Override + public void accept( + IVisitorWithContext visitor, + ContextT context) { + visitor.visitPlainMarker(this, context); + } + + /** + * Accept the {@code transformer} to visit this instance for double dispatch. + **/ + @Override + public T transform(ITransformer transformer) { + return transformer.transformPlainMarker(this); + } + + /** + * Accept the {@code transformer} to visit this instance for double dispatch + * with the {@code context}. + **/ + @Override + public T transform( + ITransformerWithContext transformer, + ContextT context) { + return transformer.transformPlainMarker(this, context); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/model/IMarker.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/model/IMarker.java new file mode 100644 index 000000000..36b0d157a --- /dev/null +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/model/IMarker.java @@ -0,0 +1,22 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.types.model; + +import dummy.common.*; +import dummy.types.enums.*; +import dummy.types.impl.*; +import dummy.types.model.*; +import java.util.List; +import dummy.types.model.IClass; + +public interface IMarker extends IClass { + // Intentionally empty. +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/model/IPlainMarker.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/model/IPlainMarker.java new file mode 100644 index 000000000..48896aec4 --- /dev/null +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/types/model/IPlainMarker.java @@ -0,0 +1,22 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.types.model; + +import dummy.common.*; +import dummy.types.enums.*; +import dummy.types.impl.*; +import dummy.types.model.*; +import java.util.List; +import dummy.types.model.IMarker; + +public interface IPlainMarker extends IMarker { + // Intentionally empty. +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/verification/Verification.java index 227a524cc..027e0c46d 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/verification/Verification.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/verification/Verification.java @@ -69,6 +69,16 @@ public Stream transformBlossom( return errorStream; } + @Override + public Stream transformPlainMarker( + IPlainMarker that) { + Stream errorStream = Stream.empty(); + + // No verification has been defined for PlainMarker. + + return errorStream; + } + @Override public Stream transformSomething( ISomething that) { diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractTransformer.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractTransformer.java index 951cc5052..86e1fb043 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractTransformer.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractTransformer.java @@ -33,6 +33,10 @@ public abstract T transformBlossom( IBlossom that ); + public abstract T transformPlainMarker( + IPlainMarker that + ); + public abstract T transformSomething( ISomething that ); diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractTransformerWithContext.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractTransformerWithContext.java index c791a6a33..4903fe931 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractTransformerWithContext.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractTransformerWithContext.java @@ -42,6 +42,11 @@ public abstract T transformBlossom( ContextT context ); + public abstract T transformPlainMarker( + IPlainMarker that, + ContextT context + ); + public abstract T transformSomething( ISomething that, ContextT context diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractVisitor.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractVisitor.java index 78d42664d..104ab80a1 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractVisitor.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractVisitor.java @@ -26,6 +26,9 @@ public abstract void visitLeaf( public abstract void visitBlossom( IBlossom that ); + public abstract void visitPlainMarker( + IPlainMarker that + ); public abstract void visitSomething( ISomething that ); diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractVisitorWithContext.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractVisitorWithContext.java index bcf7c258c..2526f3a10 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractVisitorWithContext.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/AbstractVisitorWithContext.java @@ -33,6 +33,10 @@ public abstract void visitBlossom( IBlossom that, ContextT context ); + public abstract void visitPlainMarker( + IPlainMarker that, + ContextT context + ); public abstract void visitSomething( ISomething that, ContextT context diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/ITransformer.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/ITransformer.java index cfb4f5bb9..b843ef842 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/ITransformer.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/ITransformer.java @@ -28,6 +28,9 @@ T transformLeaf( T transformBlossom( IBlossom that ); + T transformPlainMarker( + IPlainMarker that + ); T transformSomething( ISomething that ); diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/ITransformerWithContext.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/ITransformerWithContext.java index 7aa51f59e..ac9a4e278 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/ITransformerWithContext.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/ITransformerWithContext.java @@ -32,6 +32,10 @@ T transformBlossom( IBlossom that, ContextT context ); + T transformPlainMarker( + IPlainMarker that, + ContextT context + ); T transformSomething( ISomething that, ContextT context diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/IVisitor.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/IVisitor.java index c17584104..2dc7aeca6 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/IVisitor.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/IVisitor.java @@ -26,6 +26,9 @@ void visitLeaf( void visitBlossom( IBlossom that ); + void visitPlainMarker( + IPlainMarker that + ); void visitSomething( ISomething that ); diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/IVisitorWithContext.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/IVisitorWithContext.java index dceb6c9e4..c726602ee 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/IVisitorWithContext.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/IVisitorWithContext.java @@ -31,6 +31,10 @@ void visitBlossom( IBlossom that, ContextT context ); + void visitPlainMarker( + IPlainMarker that, + ContextT context + ); void visitSomething( ISomething that, ContextT context diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/VisitorThrough.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/VisitorThrough.java index 72691063c..fa1a04235 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/VisitorThrough.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/visitation/VisitorThrough.java @@ -47,6 +47,15 @@ public void visitBlossom( } } + public void visitPlainMarker( + IPlainMarker that + ) { + // Just descend through, do nothing with {@code that} + for (IClass something : that.descendOnce()) { + visit(something); + } + } + public void visitSomething( ISomething that ) { diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/xmlization/Xmlization.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/xmlization/Xmlization.java index f2d19c66d..b8cc651af 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/xmlization/Xmlization.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/main/java/dummy/xmlization/Xmlization.java @@ -623,6 +623,52 @@ private static Reporting.Result readBlossomFromElement( _DeserializeImplementation::readBlossomFromSequence); } + /** + * Deserialize an instance of IMarker from an XML element. + */ + private static Reporting.Result readIMarkerFromElement( + XMLEventReader reader) { + // NOTE (mristin): + // We only peek the name, so that the whole element can be handed on to + // the reader which we select below. + final Reporting.Result tryElementName = XmlCommon.peekElementName(reader); + if (tryElementName.isError()) { + return Reporting.Result.failure(tryElementName.getError()); + } + + switch (tryElementName.getResult()) { + case "plainMarker": + return readPlainMarkerFromElement(reader); + default: + return Reporting.Result.failure(new Reporting.Error( + "Unexpected element with the name " + tryElementName.getResult())); + } + } + + /** + * Deserialize an instance of class PlainMarker from a sequence of XML elements. + * + *

If {@code isEmptySequence} is set, we should try to deserialize + * the instance from an empty sequence. That is, the parent element + * was a self-closing element. + */ + private static Reporting.Result readPlainMarkerFromSequence( + XMLEventReader reader, + boolean isEmptySequence) { + return Reporting.Result.success(new PlainMarker()); + } + + /** + * Deserialize an instance of class PlainMarker from an XML element. + */ + private static Reporting.Result readPlainMarkerFromElement( + XMLEventReader reader) { + return XmlCommon.readNamedElement( + reader, + "plainMarker", + _DeserializeImplementation::readPlainMarkerFromSequence); + } + /** * Deserialize an instance of class Something from a sequence of XML elements. * @@ -991,6 +1037,52 @@ public static Blossom deserializeBlossom( }); } + /** + * Deserialize an instance of IMarker from {@code reader}. + * + * @param reader Initialized XML reader with reader.peek() set to the element + */ + public static IMarker deserializeIMarker( + XMLEventReader reader) { + + _DeserializeImplementation.skipStartDocument(reader); + XmlCommon.skipWhitespaceAndComments(reader); + + Reporting.Result result = + _DeserializeImplementation.readIMarkerFromElement( + reader); + + return result.onError(error -> { + error.prependSegment(new Reporting.NameSegment("imarker")); + throw new XmlCommon.DeserializeException( + Reporting.generateRelativeXPath(error.getPathSegments()), + error.getCause()); + }); + } + + /** + * Deserialize an instance of PlainMarker from {@code reader}. + * + * @param reader Initialized XML reader with reader.peek() set to the element + */ + public static PlainMarker deserializePlainMarker( + XMLEventReader reader) { + + _DeserializeImplementation.skipStartDocument(reader); + XmlCommon.skipWhitespaceAndComments(reader); + + Reporting.Result result = + _DeserializeImplementation.readPlainMarkerFromElement( + reader); + + return result.onError(error -> { + error.prependSegment(new Reporting.NameSegment("plainmarker")); + throw new XmlCommon.DeserializeException( + Reporting.generateRelativeXPath(error.getPathSegments()), + error.getCause()); + }); + } + /** * Deserialize an instance of Something from {@code reader}. * @@ -1231,6 +1323,24 @@ public void visitBlossom( _VisitorWithWriter::writeBlossomAsSequence); } + private static void writePlainMarkerAsSequence( + IPlainMarker that, + XMLStreamWriter writer) { + // Intentionally empty. + } + + @Override + public void visitPlainMarker( + IPlainMarker that, + XMLStreamWriter writer) { + XmlCommon.writeElement( + "plainMarker", + that, + writer, + withNamespace, + _VisitorWithWriter::writePlainMarkerAsSequence); + } + private static void writeSomethingAsSequence( ISomething that, XMLStreamWriter writer) { diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/CommonJsonization.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/CommonJsonization.java index f5e0c21d6..2a274083d 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/CommonJsonization.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/CommonJsonization.java @@ -94,6 +94,32 @@ public static Blossom loadMinimalBlossom() throws IOException { return Jsonization.Deserialize.deserializeBlossom(node); } // public static Blossom loadMinimalBlossom + public static PlainMarker loadMaximalPlainMarker() throws IOException { + final Path path = Paths.get( + Common.TEST_DATA_DIR, + "Json", + "Expected", + "PlainMarker", + "maximal.json"); + + final JsonNode node = CommonJson.readFromFile(path); + + return Jsonization.Deserialize.deserializePlainMarker(node); + } // public static PlainMarker loadMaximalPlainMarker + + public static PlainMarker loadMinimalPlainMarker() throws IOException { + final Path path = Paths.get( + Common.TEST_DATA_DIR, + "Json", + "Expected", + "PlainMarker", + "minimal.json"); + + final JsonNode node = CommonJson.readFromFile(path); + + return Jsonization.Deserialize.deserializePlainMarker(node); + } // public static PlainMarker loadMinimalPlainMarker + public static Something loadMaximalSomething() throws IOException { final Path path = Paths.get( Common.TEST_DATA_DIR, diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestCopying.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestCopying.java index 7d2f3146f..c3838587b 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestCopying.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestCopying.java @@ -63,6 +63,17 @@ public Boolean transformBlossom(IBlossom that, IClass other) { && that.getDetails().equals(casted.getDetails())); } + @Override + public Boolean transformPlainMarker(IPlainMarker that, IClass other) { + if (!(other instanceof PlainMarker)) { + return false; + } + + PlainMarker casted = (PlainMarker) other; + + return true; + } + @Override public Boolean transformSomething(ISomething that, IClass other) { if (!(other instanceof Something)) { @@ -119,6 +130,12 @@ private static Boolean BlossomShallowEquals( && that.getDetails().equals(other.getDetails())); } + private static Boolean PlainMarkerShallowEquals( + PlainMarker that, + PlainMarker other) { + return true; + } + private static Boolean SomethingShallowEquals( Something that, Something other) { @@ -147,6 +164,10 @@ private static Boolean BlossomDeepEquals(Blossom that, Blossom other) { return DeepEqualiserInstance.transform(that, other); } + private static Boolean PlainMarkerDeepEquals(PlainMarker that, PlainMarker other) { + return DeepEqualiserInstance.transform(that, other); + } + private static Boolean SomethingDeepEquals(Something that, Something other) { return DeepEqualiserInstance.transform(that, other); } @@ -215,6 +236,26 @@ public void testBlossomDeepCopy() throws IOException { "Blossom"); } // public void testBlossomDeepCopy + @Test + public void testPlainMarkerShallowCopy() throws IOException { + final PlainMarker instance = CommonJsonization.loadMaximalPlainMarker(); + final PlainMarker instanceCopy = Copying.shallow(instance); + + assertTrue( + PlainMarkerShallowEquals(instance, instanceCopy), + "PlainMarker"); + } // public void testPlainMarkerShallowCopy + + @Test + public void testPlainMarkerDeepCopy() throws IOException { + final PlainMarker instance = CommonJsonization.loadMaximalPlainMarker(); + final PlainMarker instanceCopy = Copying.deep(instance); + + assertTrue( + PlainMarkerDeepEquals(instance, instanceCopy), + "PlainMarker"); + } // public void testPlainMarkerDeepCopy + @Test public void testSomethingShallowCopy() throws IOException { final Something instance = CommonJsonization.loadMaximalSomething(); diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestDescendAndVisitorThrough.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestDescendAndVisitorThrough.java index 13792ea3f..29761d829 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestDescendAndVisitorThrough.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestDescendAndVisitorThrough.java @@ -136,6 +136,27 @@ public void testDescendAgainstVisitorThroughForBlossom() throws IOException { assertDescendAndVisitorThroughSame(instance); } // public void testDescendAgainstVisitorThroughForBlossom + @Test + public void testDescendOfPlainMarker() throws IOException { + final PlainMarker instance = CommonJsonization.loadMaximalPlainMarker(); + + compareOrRerecordTrace( + instance, + Paths.get( + Common.TEST_DATA_DIR, + "Descend", + "PlainMarker", + "maximal.json.trace")); + } // public void testDescendOfPlainMarker + + @Test + public void testDescendAgainstVisitorThroughForPlainMarker() throws IOException { + PlainMarker instance = ( + CommonJsonization.loadMaximalPlainMarker()); + + assertDescendAndVisitorThroughSame(instance); + } // public void testDescendAgainstVisitorThroughForPlainMarker + @Test public void testDescendOfSomething() throws IOException { final Something instance = CommonJsonization.loadMaximalSomething(); diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestDescendOnce.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestDescendOnce.java index b429bb60c..86085483e 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestDescendOnce.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestDescendOnce.java @@ -83,6 +83,19 @@ public void testBlossom() throws IOException { "maximal.json.trace")); } // public void testBlossom + @Test + public void testPlainMarker() throws IOException { + PlainMarker instance = CommonJsonization.loadMaximalPlainMarker(); + + compareOrRerecordTrace( + instance, + Paths.get( + Common.TEST_DATA_DIR, + "DescendOnce", + "PlainMarker", + "maximal.json.trace")); + } // public void testPlainMarker + @Test public void testSomething() throws IOException { Something instance = CommonJsonization.loadMaximalSomething(); diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestEnhancing.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestEnhancing.java index 360c39db7..c6f53075d 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestEnhancing.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestEnhancing.java @@ -130,6 +130,37 @@ public void testBlossom() throws IOException { .orElseThrow(() -> new IllegalStateException("Missing max value for wrapped."))); } // public void testBlossom + @Test + public void testPlainMarker() throws IOException { + final PlainMarker instance = CommonJsonization.loadMaximalPlainMarker(); + + final Enhancer<_Enhancement> enhancer = createEnhancer(); + + assert !enhancer.unwrap(instance).isPresent(); + + final IClass wrapped = enhancer.wrap(instance); + assertNotNull(wrapped); + + final Set idSet = new HashSet<>(); + idSet.add(enhancer.mustUnwrap(wrapped).someCustomId); + wrapped + .descend() + .forEach(descendant -> idSet.add(enhancer.mustUnwrap(descendant).someCustomId)); + + assertFalse(enhancer.unwrap(instance).isPresent()); + assertNotNull(wrapped); + assertEquals( + 1L, + idSet.stream() + .min(Comparator.comparing(Long::valueOf)) + .orElseThrow(() -> new IllegalStateException("Missing min value for wrapped."))); + assertEquals( + idSet.size(), + idSet.stream() + .max(Comparator.comparing(Long::valueOf)) + .orElseThrow(() -> new IllegalStateException("Missing max value for wrapped."))); + } // public void testPlainMarker + @Test public void testSomething() throws IOException { final Something instance = CommonJsonization.loadMaximalSomething(); diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestJsonizationOfConcreteClasses.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestJsonizationOfConcreteClasses.java index de7545754..bad030bc2 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestJsonizationOfConcreteClasses.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestJsonizationOfConcreteClasses.java @@ -379,6 +379,105 @@ public void testBlossomVerificationFail() throws IOException { } } // public void testBlossomVerificationFail + @Test + public void testPlainMarkerOk() throws IOException { + final ObjectMapper objectMapper = new ObjectMapper(); + + final Path searchPath = Paths.get( + Common.TEST_DATA_DIR, + "Json", + "Expected", + "PlainMarker"); + final List paths = Common.findPaths(searchPath, ".json"); + + for (Path path : paths) { + final JsonNode node = objectMapper.readTree(path.toFile()); + final PlainMarker instance = Jsonization.Deserialize.deserializePlainMarker(node); + + final Iterable errorIter = Verification.verify(instance); + final List errors = Common.asList(errorIter); + Common.assertNoVerificationErrors(errors, path); + } + } // public void testPlainMarkerOk + + @Test + public void testPlainMarkerDeserializationFromNonObjectFail() throws IOException { + final JsonNode node = JsonNodeFactory.instance.textNode("INVALID"); + + Jsonization.DeserializeException exception = null; + try { + final PlainMarker unused = Jsonization.Deserialize.deserializePlainMarker(node); + } catch (Jsonization.DeserializeException observedException) { + exception = observedException; + } + + assert exception != null : "Expected an exception, but got none"; + assert exception.getMessage().startsWith("Expected a JsonObject, but got ") : + "Unexpected exception message: " + exception.getMessage(); + } // public void testPlainMarkerDeserializationFromNonObjectFail + + @Test + public void testPlainMarkerDeserializationFail() throws IOException { + for (Path causeDir : + Common.findDirs( + Paths.get( + Common.TEST_DATA_DIR, + "Json", + "Unexpected", + "Unserializable"))) { + final Path clsDir = causeDir.resolve("PlainMarker"); + + if (!Files.exists(clsDir)) { + // No examples of PlainMarker for the failure cause. + continue; + } + + final List paths = Common.findPaths(clsDir, ".json"); + for (Path path : paths) { + final JsonNode node = CommonJson.readFromFile(path); + + Jsonization.DeserializeException exception = null; + try { + final PlainMarker var = Jsonization.Deserialize.deserializePlainMarker(node); + } catch (Jsonization.DeserializeException observedException) { + exception = observedException; + } + + assertEqualsExpectedOrRerecordDeserializationException( + exception, path); + } + } + } // public void testPlainMarkerDeserializationFail + + @Test + public void testPlainMarkerVerificationFail() throws IOException { + for (Path causeDir : + Common.findDirs( + Paths.get( + Common.TEST_DATA_DIR, + "Json", + "Unexpected", + "Invalid"))) { + final Path clsDir = causeDir.resolve("PlainMarker"); + + if (!Files.exists(clsDir)) { + // No examples of PlainMarker for the failure cause. + continue; + } + + final List paths = Common.findPaths(clsDir, ".json"); + for (Path path : paths) { + final JsonNode node = CommonJson.readFromFile(path); + + final PlainMarker instance = Jsonization.Deserialize.deserializePlainMarker(node); + + final Iterable errorIter = Verification.verify(instance); + final List errors = Common.asList(errorIter); + Common.assertEqualsExpectedOrRerecordVerificationErrors(errors, path); + } + } + } // public void testPlainMarkerVerificationFail + @Test public void testSomethingOk() throws IOException { final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestJsonizationOfInterfaces.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestJsonizationOfInterfaces.java index a713cf90d..71b1ada41 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestJsonizationOfInterfaces.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestJsonizationOfInterfaces.java @@ -145,6 +145,22 @@ public void testRoundTripILeafFromLeaf() assertEquals( objectMapper.readTree(jsonObject.toString()), objectMapper.readTree(anotherJsonObject.toString())); } // void testRoundTripILeafFromLeaf + + @Test + public void testRoundTripIMarkerFromPlainMarker() + throws IOException, JsonProcessingException { + final ObjectMapper objectMapper = new ObjectMapper(); + + PlainMarker instance = CommonJsonization.loadMaximalPlainMarker(); + final JsonNode jsonObject = Jsonization.Serialize.toJsonObject(instance); + + IMarker anotherInstance = Jsonization.Deserialize.deserializeIMarker( + jsonObject); + final JsonNode anotherJsonObject = Jsonization.Serialize.toJsonObject(anotherInstance); + + assertEquals( + objectMapper.readTree(jsonObject.toString()), objectMapper.readTree(anotherJsonObject.toString())); + } // void testRoundTripIMarkerFromPlainMarker } // class TestJsonizationOfInterfaces // package dummy.tests diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestXmlizationOfConcreteClasses.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestXmlizationOfConcreteClasses.java index b00f9be50..c7bed6396 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestXmlizationOfConcreteClasses.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestXmlizationOfConcreteClasses.java @@ -486,6 +486,99 @@ public void testBlossomVerificationFail() throws IOException, XMLStreamException } } // public void testBlossomVerificationFail + @Test + public void testPlainMarkerOk() throws IOException, XMLStreamException { + final Path searchPath = + Paths.get(Common.TEST_DATA_DIR, "Xml", "Expected", "plainMarker"); + final List paths = Common.findPaths(searchPath, ".xml"); + + for (Path path : paths) { + final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); + final XMLEventReader xmlReader = + xmlInputFactory.createXMLEventReader(Files.newInputStream(path)); + + final PlainMarker instance = + Xmlization.Deserialize.deserializePlainMarker(xmlReader); + + final Iterable errorIter = Verification.verify(instance); + final List errors = Common.asList(errorIter); + Common.assertNoVerificationErrors(errors, path); + + assertSerializeDeserializeEqualsOriginal(instance, path); + } + } // public void testPlainMarkerOk + + @Test + public void testPlainMarkerDeserializationFail() throws IOException, XMLStreamException { + for ( + Path causeDir : + Common.findDirs( + Paths.get( + Common.TEST_DATA_DIR, + "Xml", + "Unexpected", + "Unserializable"))) { + final Path clsDir = + causeDir.resolve("plainMarker"); + + if (!Files.exists(clsDir)) { + // No examples of PlainMarker for the failure cause. + continue; + } + + final List paths = Common.findPaths(clsDir, ".xml"); + for (Path path : paths) { + final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); + final XMLEventReader xmlReader = + xmlInputFactory.createXMLEventReader(Files.newInputStream(path)); + + XmlCommon.DeserializeException exception = null; + + try { + Xmlization.Deserialize.deserializePlainMarker(xmlReader); + } catch (XmlCommon.DeserializeException observedException) { + exception = observedException; + } + + assertEqualsExpectedOrRerecordDeserializationException(exception, path); + } + } + } // public void testPlainMarkerDeserializationFail + + @Test + public void testPlainMarkerVerificationFail() throws IOException, XMLStreamException { + for ( + Path causeDir : + Common.findDirs( + Paths.get( + Common.TEST_DATA_DIR, + "Xml", + "Unexpected", + "Invalid"))) { + final Path clsDir = causeDir.resolve( + "plainMarker"); + + if (!Files.exists(clsDir)) { + // No examples of PlainMarker for the failure cause. + continue; + } + + final List paths = Common.findPaths(clsDir, ".xml"); + for (Path path : paths) { + final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); + final XMLEventReader xmlReader = + xmlInputFactory.createXMLEventReader(Files.newInputStream(path)); + + final PlainMarker instance = + Xmlization.Deserialize.deserializePlainMarker(xmlReader); + + final Iterable errorIter = Verification.verify(instance); + final List errors = Common.asList(errorIter); + Common.assertEqualsExpectedOrRerecordVerificationErrors(errors, path); + } + } + } // public void testPlainMarkerVerificationFail + @Test public void testSomethingOk() throws IOException, XMLStreamException { final Path searchPath = diff --git a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestXmlizationOfInterfaces.java b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestXmlizationOfInterfaces.java index 58786c4c2..e93db9603 100644 --- a/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestXmlizationOfInterfaces.java +++ b/dev/test_data/main/java/expected/deep_class_hierarchy/expected_output/src/test/java/TestXmlizationOfInterfaces.java @@ -272,6 +272,38 @@ public void testRoundTripILeafFromLeaf() // Compare assertEquals(outputText, anotherStringOut.toString()); } // void testRoundTripILeafFromLeaf + + @Test + public void testRoundTripIMarkerFromPlainMarker() + throws IOException, XMLStreamException { + // We load from JSON here just to jump-start the round trip. + // The round-trip goes then over XML. + final IMarker instance = + CommonJsonization.loadMaximalPlainMarker(); + + // The round-trip starts here. + final StringWriter stringOut = new StringWriter(); + final XMLOutputFactory outputFactory = XMLOutputFactory.newFactory(); + final XMLStreamWriter xmlWriter = outputFactory.createXMLStreamWriter(stringOut); + + Xmlization.Serialize.to(instance, xmlWriter); + String outputText = stringOut.toString(); + + // De-serialize from XML + final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); + final XMLEventReader xmlReader = + xmlInputFactory.createXMLEventReader(new StringReader(outputText)); + final IMarker anotherInstance = + Xmlization.Deserialize.deserializeIMarker(xmlReader); + + // Serialize back to XML + final StringWriter anotherStringOut = new StringWriter(); + final XMLStreamWriter anotherXmlWriter = outputFactory.createXMLStreamWriter(anotherStringOut); + Xmlization.Serialize.to(anotherInstance, anotherXmlWriter); + + // Compare + assertEquals(outputText, anotherStringOut.toString()); + } // void testRoundTripIMarkerFromPlainMarker } // class TestXmlizationOfInterfaces // package dummy.tests diff --git a/dev/test_data/main/jsonschema/expected/deep_class_hierarchy/expected_output/schema.json b/dev/test_data/main/jsonschema/expected/deep_class_hierarchy/expected_output/schema.json index d58fd4d1b..1130c409f 100644 --- a/dev/test_data/main/jsonschema/expected/deep_class_hierarchy/expected_output/schema.json +++ b/dev/test_data/main/jsonschema/expected/deep_class_hierarchy/expected_output/schema.json @@ -129,12 +129,24 @@ } ] }, + "Marker": { + "type": "object", + "properties": { + "modelType": { + "$ref": "#/definitions/ModelType" + } + }, + "required": [ + "modelType" + ] + }, "ModelType": { "type": "string", "enum": [ "Blossom", "Branch", - "Leaf" + "Leaf", + "PlainMarker" ] }, "Node": { @@ -165,6 +177,20 @@ } ] }, + "PlainMarker": { + "allOf": [ + { + "$ref": "#/definitions/Marker" + }, + { + "properties": { + "modelType": { + "const": "PlainMarker" + } + } + } + ] + }, "Something": { "type": "object", "properties": { diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/common_jsonization.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/common_jsonization.py index 7eec079a9..ebe6c370e 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/common_jsonization.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/common_jsonization.py @@ -128,6 +128,8 @@ def check_equal( our_jsonization.container_from_jsonable, 'Leaf': our_jsonization.leaf_from_jsonable, + 'PlainMarker': + our_jsonization.plain_marker_from_jsonable, 'Something': our_jsonization.something_from_jsonable } @@ -145,6 +147,8 @@ def check_equal( our_types.Container, 'Leaf': our_types.Leaf, + 'PlainMarker': + our_types.PlainMarker, 'Something': our_types.Something } diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_jsonization_of_classes_with_descendants.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_jsonization_of_classes_with_descendants.py index 71e396684..714608ce7 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_jsonization_of_classes_with_descendants.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_jsonization_of_classes_with_descendants.py @@ -107,6 +107,34 @@ def test_leaf(self) -> None: self.assertListEqual([], list(map(str, mismatch))) + def test_marker(self) -> None: + for descendant_model_type in [ + 'PlainMarker' + ]: + for path in sorted( + ( + tests.common.TEST_DATA_DIR + / "Json" + / "Expected" + / descendant_model_type + ).glob("**/*.json") + ): + with path.open("rt") as fid: + original_jsonable = json.load(fid) + + instance = our_jsonization.marker_from_jsonable( + original_jsonable + ) + + another_jsonable = our_jsonization.to_jsonable(instance) + + mismatch = tests.common_jsonization.check_equal( + original_jsonable, + another_jsonable + ) + + self.assertListEqual([], list(map(str, mismatch))) + if __name__ == "__main__": unittest.main() diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_jsonization_of_concrete_classes.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_jsonization_of_concrete_classes.py index 4956913af..9e29a8430 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_jsonization_of_concrete_classes.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_jsonization_of_concrete_classes.py @@ -94,6 +94,30 @@ def test_blossom(self) -> None: ) self.assertListEqual([], list(map(str, mismatches))) + def test_plain_marker(self) -> None: + for path in sorted( + ( + tests.common.TEST_DATA_DIR + / "Json" + / "Expected" + / 'PlainMarker' + ).glob("**/*.json") + ): + with path.open("rt") as fid: + original_jsonable = json.load(fid) + + instance = our_jsonization.plain_marker_from_jsonable( + original_jsonable + ) + + another_jsonable = our_jsonization.to_jsonable(instance) + + mismatches = tests.common_jsonization.check_equal( + original_jsonable, + another_jsonable + ) + self.assertListEqual([], list(map(str, mismatches))) + def test_something(self) -> None: for path in sorted( ( diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_verification.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_verification.py index 6a40cc895..23c4c65eb 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_verification.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_verification.py @@ -95,6 +95,31 @@ def test_blossom(self) -> None: ) ) + def test_plain_marker(self) -> None: + for path in sorted( + ( + tests.common.TEST_DATA_DIR + / "Json" + / "Expected" + / 'PlainMarker' + ).glob("**/*.json") + ): + with path.open("rt") as fid: + jsonable = json.load(fid) + + instance = our_jsonization.plain_marker_from_jsonable(jsonable) + + errors = list(our_verification.verify(instance)) + + if len(errors) > 0: + self.fail( + f"Expected no errors when verifying the instance de-serialized " + f"from {path}, but got {len(errors)} error(s):\n" + + "\n".join( + f"{error.path}: {error.cause}" for error in errors + ) + ) + def test_something(self) -> None: for path in sorted( ( @@ -270,6 +295,47 @@ def test_blossom(self) -> None: tests.common.record_or_check(expected_path, got) + def test_plain_marker(self) -> None: + for cause_dir in sorted( + ( + tests.common.TEST_DATA_DIR + / "Json" + / "Unexpected" + / "Invalid" + ).iterdir() + ): + for path in sorted( + (cause_dir / 'PlainMarker').glob("**/*.json") + ): + rel_path = path.relative_to(tests.common.TEST_DATA_DIR) + + expected_path = ( + tests.common.TEST_DATA_DIR + / "VerificationError" + / rel_path.parent + / f"{rel_path.name}.errors" + ) + + with path.open("rt") as fid: + jsonable = json.load(fid) + + instance = our_jsonization.plain_marker_from_jsonable(jsonable) + + errors = list(our_verification.verify(instance)) + + if len(errors) == 0: + self.fail( + f"Expected at least one verification error " + f"when verifying the instance de-serialized " + f"from {path}, but got none" + ) + + got = "\n".join( + f"{error.path}: {error.cause}" for error in errors + ) + "\n" + + tests.common.record_or_check(expected_path, got) + def test_something(self) -> None: for cause_dir in sorted( ( diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_xmlization_of_classes_with_descendants.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_xmlization_of_classes_with_descendants.py index d3a3bb2d7..7031aec86 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_xmlization_of_classes_with_descendants.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_xmlization_of_classes_with_descendants.py @@ -176,6 +176,56 @@ def test_leaf(self) -> None: tests.common_xmlization.assert_elements_equal(et_concrete, et_from_str) # endregion + def test_marker(self) -> None: + for xml_class_name_of_descendant in [ + 'plainMarker' + ]: + for path in sorted( + ( + tests.common.TEST_DATA_DIR + / "Xml" + / "Expected" + / xml_class_name_of_descendant + ).glob("**/*.xml") + ): + text = path.read_text(encoding="utf-8") + et_concrete = ET.fromstring(text) + tests.common_xmlization.remove_redundant_whitespace(et_concrete) + + # region From iterparse + iterator = ET.iterparse(source=io.StringIO(text), events=["start", "end"]) + got_from_iterparse = our_xmlization.marker_from_iterparse(iterator) + + et_from_iterparse = ET.fromstring(our_xmlization.to_str(got_from_iterparse)) + tests.common_xmlization.remove_redundant_whitespace(et_from_iterparse) + tests.common_xmlization.assert_elements_equal(et_concrete, et_from_iterparse) + # endregion + + # region From stream + got_from_stream = our_xmlization.marker_from_stream(io.StringIO(text)) + et_from_stream = ET.fromstring(our_xmlization.to_str(got_from_stream)) + tests.common_xmlization.remove_redundant_whitespace(et_from_stream) + tests.common_xmlization.assert_elements_equal(et_concrete, et_from_stream) + # endregion + + # region From file + with tempfile.TemporaryDirectory() as tmp_dir: + path = pathlib.Path(tmp_dir) / "something.xml" + path.write_text(text, encoding="utf-8") + + got_from_file = our_xmlization.marker_from_file(path) + et_from_file = ET.fromstring(our_xmlization.to_str(got_from_file)) + tests.common_xmlization.remove_redundant_whitespace(et_from_file) + tests.common_xmlization.assert_elements_equal(et_concrete, et_from_file) + # endregion + + # region From string + got_from_str = our_xmlization.marker_from_str(text) + et_from_str = ET.fromstring(our_xmlization.to_str(got_from_str)) + tests.common_xmlization.remove_redundant_whitespace(et_from_str) + tests.common_xmlization.assert_elements_equal(et_concrete, et_from_str) + # endregion + if __name__ == "__main__": unittest.main() diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_xmlization_of_concrete_classes.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_xmlization_of_concrete_classes.py index 7df34643e..2ae7d9132 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_xmlization_of_concrete_classes.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dev/tests/test_xmlization_of_concrete_classes.py @@ -205,6 +205,66 @@ def test_blossom(self) -> None: tests.common_xmlization.assert_elements_equal(et_concrete, et_from_str) # endregion + def test_plain_marker(self) -> None: + for path in sorted( + ( + tests.common.TEST_DATA_DIR + / "Xml" + / "Expected" + / 'plainMarker' + ).glob("**/*.xml") + ): + text = path.read_text(encoding="utf-8") + et_concrete = ET.fromstring(text) + tests.common_xmlization.remove_redundant_whitespace(et_concrete) + + # region From iterparse + iterator = ET.iterparse(source=io.StringIO(text), events=["start", "end"]) + got_from_iterparse = ( + our_xmlization.plain_marker_from_iterparse(iterator) + ) + + et_from_iterparse = ET.fromstring(our_xmlization.to_str(got_from_iterparse)) + tests.common_xmlization.remove_redundant_whitespace(et_from_iterparse) + tests.common_xmlization.assert_elements_equal(et_concrete, et_from_iterparse) + # endregion + + # region From stream + got_from_stream = ( + our_xmlization + .plain_marker_from_stream( + io.StringIO(text) + ) + ) + et_from_stream = ET.fromstring(our_xmlization.to_str(got_from_stream)) + tests.common_xmlization.remove_redundant_whitespace(et_from_stream) + tests.common_xmlization.assert_elements_equal(et_concrete, et_from_stream) + # endregion + + # region From file + with tempfile.TemporaryDirectory() as tmp_dir: + path = pathlib.Path(tmp_dir) / "something.xml" + path.write_text(text, encoding="utf-8") + + got_from_file = ( + our_xmlization + .plain_marker_from_file(path) + ) + et_from_file = ET.fromstring(our_xmlization.to_str(got_from_file)) + tests.common_xmlization.remove_redundant_whitespace(et_from_file) + tests.common_xmlization.assert_elements_equal(et_concrete, et_from_file) + # endregion + + # region From string + got_from_str = ( + our_xmlization + .plain_marker_from_str(text) + ) + et_from_str = ET.fromstring(our_xmlization.to_str(got_from_str)) + tests.common_xmlization.remove_redundant_whitespace(et_from_str) + tests.common_xmlization.assert_elements_equal(et_concrete, et_from_str) + # endregion + def test_something(self) -> None: for path in sorted( ( diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/jsonization.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/jsonization.py index e95809063..f5c48a42a 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/jsonization.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/jsonization.py @@ -598,6 +598,62 @@ def blossom_from_jsonable( ) +def marker_from_jsonable( + jsonable: Jsonable +) -> our_types.Marker: + """ + Parse an instance of :py:class:`.types.Marker` from the JSON-able + structure :paramref:`jsonable`. + + :param jsonable: structure to be parsed + :return: Concrete instance of :py:class:`.types.Marker` + :raise: :py:class:`DeserializationException` if unexpected :paramref:`jsonable` + """ + return _dispatch_from_jsonable( + jsonable, + _MARKER_FROM_JSONABLE_DISPATCH, + 'Marker' + ) + + +def plain_marker_from_jsonable( + jsonable: Jsonable +) -> our_types.PlainMarker: + """ + Parse an instance of :py:class:`.types.PlainMarker` from the JSON-able + structure :paramref:`jsonable`. + + :param jsonable: structure to be parsed + :return: Parsed instance of :py:class:`.types.PlainMarker` + :raise: :py:class:`DeserializationException` if unexpected :paramref:`jsonable` + """ + mapping = _as_mapping(jsonable) + + model_type = mapping.get('modelType', None) + if model_type != 'PlainMarker': + raise DeserializationException( + f"Expected modelType to be 'PlainMarker', " + f"but got: {model_type!r}" + ) + + try: + for key in mapping: + if key == 'modelType': + # The model type has already been checked above. + pass + else: + raise DeserializationException( + f"Unexpected property: {key}" + ) + except DeserializationException as exception: + exception.path._prepend( + PropertySegment(mapping, key) + ) + raise + + return our_types.PlainMarker() + + def something_from_jsonable( jsonable: Jsonable ) -> our_types.Something: @@ -735,6 +791,16 @@ def container_from_jsonable( } +#: De-serialize a concrete instance of +#: :py:class:`.types.Marker`, by its model type +_MARKER_FROM_JSONABLE_DISPATCH: Mapping[ + str, + _Parser[our_types.Marker] +] = { + 'PlainMarker': plain_marker_from_jsonable, +} + + # endregion @@ -891,6 +957,15 @@ def _blossom_to_jsonable( return jsonable +def _plain_marker_to_jsonable( + that: our_types.PlainMarker +) -> MutableMapping[str, MutableJsonable]: + """Serialize :paramref:`that` to a JSON-able representation.""" + jsonable: MutableMapping[str, MutableJsonable] = dict() + jsonable['modelType'] = 'PlainMarker' + return jsonable + + def _something_to_jsonable( that: our_types.Something ) -> MutableMapping[str, MutableJsonable]: @@ -950,6 +1025,9 @@ class _Serializer( transform_blossom = staticmethod( _blossom_to_jsonable ) + transform_plain_marker = staticmethod( + _plain_marker_to_jsonable + ) transform_something = staticmethod( _something_to_jsonable ) diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/types.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/types.py index 260e8829e..b3971bfce 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/types.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/types.py @@ -346,6 +346,84 @@ def __init__( self.details = details +class Marker(Class): + # pylint: disable=missing-class-docstring + pass + + +class PlainMarker(Marker): + # pylint: disable=missing-class-docstring + + def descend_once(self) -> Iterator[Class]: + """ + Iterate over the instances referenced from this instance. + + We do not recurse into the referenced instance. + + :yield: instances directly referenced from this instance + """ + return self._descend(recurse=False) + + def descend(self) -> Iterator[Class]: + """ + Iterate recursively over the instances referenced from this one. + + :yield: instances recursively referenced from this instance + """ + return self._descend(recurse=True) + + def _descend(self, recurse: bool) -> Iterator[Class]: + """ + Iterate over the instances referenced from this one, and recursively + over their descendants if :paramref:`recurse` is set. + + :param recurse: if set, descend recursively into the referenced instances + :yield: instances referenced from this instance + """ + # No descendable properties + return + # For this uncommon return-yield construction, see: + # https://stackoverflow.com/questions/13243766/how-to-define-an-empty-generator-function + # noinspection PyUnreachableCode + yield + + def accept(self, visitor: "AbstractVisitor") -> None: + """Dispatch the :paramref:`visitor` on this instance.""" + visitor.visit_plain_marker(self) + + def accept_with_context( + self, + visitor: "AbstractVisitorWithContext[ContextT]", + context: ContextT + ) -> None: + """Dispatch the :paramref:`visitor` on this instance in :paramref:`context`.""" + visitor.visit_plain_marker_with_context(self, context) + + def transform( + self, + transformer: "AbstractTransformer[T]" + ) -> T: + """Dispatch the :paramref:`transformer` on this instance.""" + return transformer.transform_plain_marker(self) + + def transform_with_context( + self, + transformer: "AbstractTransformerWithContext[ContextT, T]", + context: ContextT + ) -> T: + """ + Dispatch the :paramref:`transformer` on this instance in :paramref:`context`. + """ + return transformer.transform_plain_marker_with_context( + self, context) + + def __init__(self) -> None: + """Initialize with the given values.""" + Marker.__init__( + self + ) + + class Something(Class): # pylint: disable=missing-class-docstring @@ -545,6 +623,14 @@ def visit_blossom( """Visit :paramref:`that`.""" raise NotImplementedError() + @abc.abstractmethod + def visit_plain_marker( + self, + that: PlainMarker + ) -> None: + """Visit :paramref:`that`.""" + raise NotImplementedError() + @abc.abstractmethod def visit_something( self, @@ -599,6 +685,15 @@ def visit_blossom_with_context( """Visit :paramref:`that` in :paramref:`context`.""" raise NotImplementedError() + @abc.abstractmethod + def visit_plain_marker_with_context( + self, + that: PlainMarker, + context: ContextT + ) -> None: + """Visit :paramref:`that` in :paramref:`context`.""" + raise NotImplementedError() + @abc.abstractmethod def visit_something_with_context( self, @@ -656,6 +751,14 @@ def visit_blossom( for another in that.descend_once(): self.visit(another) + def visit_plain_marker( + self, + that: PlainMarker + ) -> None: + """Visit :paramref:`that`.""" + for another in that.descend_once(): + self.visit(another) + def visit_something( self, that: Something @@ -717,6 +820,15 @@ def visit_blossom_with_context( for another in that.descend_once(): self.visit_with_context(another, context) + def visit_plain_marker_with_context( + self, + that: PlainMarker, + context: ContextT + ) -> None: + """Visit :paramref:`that` in :paramref:`context`.""" + for another in that.descend_once(): + self.visit_with_context(another, context) + def visit_something_with_context( self, that: Something, @@ -769,6 +881,14 @@ def transform_blossom( """Transform :paramref:`that`.""" raise NotImplementedError() + @abc.abstractmethod + def transform_plain_marker( + self, + that: PlainMarker + ) -> T: + """Transform :paramref:`that`.""" + raise NotImplementedError() + @abc.abstractmethod def transform_something( self, @@ -825,6 +945,15 @@ def transform_blossom_with_context( """Transform :paramref:`that` in :paramref:`context`.""" raise NotImplementedError() + @abc.abstractmethod + def transform_plain_marker_with_context( + self, + that: PlainMarker, + context: ContextT + ) -> T: + """Transform :paramref:`that` in :paramref:`context`.""" + raise NotImplementedError() + @abc.abstractmethod def transform_something_with_context( self, @@ -886,6 +1015,13 @@ def transform_blossom( """Transform :paramref:`that`.""" return self.default + def transform_plain_marker( + self, + that: PlainMarker + ) -> T: + """Transform :paramref:`that`.""" + return self.default + def transform_something( self, that: Something @@ -949,6 +1085,14 @@ def transform_blossom_with_context( """Transform :paramref:`that` in :paramref:`context`.""" return self.default + def transform_plain_marker_with_context( + self, + that: PlainMarker, + context: ContextT + ) -> T: + """Transform :paramref:`that` in :paramref:`context`.""" + return self.default + def transform_something_with_context( self, that: Something, diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/verification.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/verification.py index 534f54f94..7ad5dc204 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/verification.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/verification.py @@ -103,6 +103,18 @@ def transform_blossom( # noinspection PyUnreachableCode yield + # noinspection PyMethodMayBeStatic + def transform_plain_marker( + self, + that: our_types.PlainMarker + ) -> Iterator[Error]: + # No verification has been defined for PlainMarker. + return + # For this uncommon return-yield construction, see: + # https://stackoverflow.com/questions/13243766/how-to-define-an-empty-generator-function + # noinspection PyUnreachableCode + yield + # noinspection PyMethodMayBeStatic def transform_something( self, diff --git a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/xmlization.py b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/xmlization.py index e5f394c58..d75621154 100644 --- a/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/xmlization.py +++ b/dev/test_data/main/python/expected/deep_class_hierarchy/expected_output/dummy/xmlization.py @@ -875,6 +875,364 @@ def blossom_from_str( ) +def marker_from_iterparse( + iterator: Iterator[Tuple[str, Element]] +) -> our_types.Marker: + """ + Read an instance of :py:class:`.types.Marker` from + the :paramref:`iterator`. + + Example usage: + + .. code-block:: + + import pathlib + import xml.etree.ElementTree as ET + + import dummy.xmlization as our_xmlization + + path = pathlib.Path(...) + with path.open("rt") as fid: + iterator = ET.iterparse( + source=fid, + events=['start', 'end'] + ) + instance = our_xmlization.marker_from_iterparse( + iterator + ) + + # Do something with the ``instance`` + + :param iterator: + Input stream of ``(event, element)`` coming from + :py:func:`xml.etree.ElementTree.iterparse` with the argument + ``events=["start", "end"]`` + :raise: :py:class:`DeserializationException` if unexpected input + :return: + Instance of :py:class:`.types.Marker` read from + :paramref:`iterator` + """ + return _read_instance_from_iterparse( + iterator, + _read_marker_as_element, + 'Marker' + ) + + +def marker_from_stream( + stream: TextIO, + has_iterparse: HasIterparse = xml.etree.ElementTree +) -> our_types.Marker: + """ + Read an instance of :py:class:`.types.Marker` from + the :paramref:`stream`. + + Example usage: + + .. code-block:: + + import dummy.xmlization as our_xmlization + + with open_some_stream_over_network(...) as stream: + instance = our_xmlization.marker_from_stream( + stream + ) + + # Do something with the ``instance`` + + :param stream: + representing an instance of + :py:class:`.types.Marker` in XML + :param has_iterparse: + Module containing ``iterparse`` function. + + Default is to use :py:mod:`xml.etree.ElementTree` from the standard + library. If you have to deal with malicious input, consider using + a library such as `defusedxml.ElementTree`_. + :raise: :py:class:`DeserializationException` if unexpected input + :return: + Instance of :py:class:`.types.Marker` read from + :paramref:`stream` + """ + iterator = has_iterparse.iterparse( + stream, + ['start', 'end'] + ) + return marker_from_iterparse( + _with_elements_cleared_after_yield(iterator) + ) + + +def marker_from_file( + path: PathLike, + has_iterparse: HasIterparse = xml.etree.ElementTree +) -> our_types.Marker: + """ + Read an instance of :py:class:`.types.Marker` from + the :paramref:`path`. + + Example usage: + + .. code-block:: + + import pathlib + import dummy.xmlization as our_xmlization + + path = pathlib.Path(...) + instance = our_xmlization.marker_from_file( + path + ) + + # Do something with the ``instance`` + + :param path: + to the file representing an instance of + :py:class:`.types.Marker` in XML + :param has_iterparse: + Module containing ``iterparse`` function. + + Default is to use :py:mod:`xml.etree.ElementTree` from the standard + library. If you have to deal with malicious input, consider using + a library such as `defusedxml.ElementTree`_. + :raise: :py:class:`DeserializationException` if unexpected input + :return: + Instance of :py:class:`.types.Marker` read from + :paramref:`path` + """ + with open(os.fspath(path), "rt", encoding='utf-8') as fid: + iterator = has_iterparse.iterparse( + fid, + ['start', 'end'] + ) + return marker_from_iterparse( + _with_elements_cleared_after_yield(iterator) + ) + + +def marker_from_str( + text: str, + has_iterparse: HasIterparse = xml.etree.ElementTree +) -> our_types.Marker: + """ + Read an instance of :py:class:`.types.Marker` from + the :paramref:`text`. + + Example usage: + + .. code-block:: + + import pathlib + import dummy.xmlization as our_xmlization + + text = "<...>..." + instance = our_xmlization.marker_from_str( + text + ) + + # Do something with the ``instance`` + + :param text: + representing an instance of + :py:class:`.types.Marker` in XML + :param has_iterparse: + Module containing ``iterparse`` function. + + Default is to use :py:mod:`xml.etree.ElementTree` from the standard + library. If you have to deal with malicious input, consider using + a library such as `defusedxml.ElementTree`_. + :raise: :py:class:`DeserializationException` if unexpected input + :return: + Instance of :py:class:`.types.Marker` read from + :paramref:`text` + """ + iterator = has_iterparse.iterparse( + io.StringIO(text), + ['start', 'end'] + ) + return marker_from_iterparse( + _with_elements_cleared_after_yield(iterator) + ) + + +def plain_marker_from_iterparse( + iterator: Iterator[Tuple[str, Element]] +) -> our_types.PlainMarker: + """ + Read an instance of :py:class:`.types.PlainMarker` from + the :paramref:`iterator`. + + Example usage: + + .. code-block:: + + import pathlib + import xml.etree.ElementTree as ET + + import dummy.xmlization as our_xmlization + + path = pathlib.Path(...) + with path.open("rt") as fid: + iterator = ET.iterparse( + source=fid, + events=['start', 'end'] + ) + instance = our_xmlization.plain_marker_from_iterparse( + iterator + ) + + # Do something with the ``instance`` + + :param iterator: + Input stream of ``(event, element)`` coming from + :py:func:`xml.etree.ElementTree.iterparse` with the argument + ``events=["start", "end"]`` + :raise: :py:class:`DeserializationException` if unexpected input + :return: + Instance of :py:class:`.types.PlainMarker` read from + :paramref:`iterator` + """ + return _read_instance_from_iterparse( + iterator, + _read_plain_marker_as_element, + 'PlainMarker' + ) + + +def plain_marker_from_stream( + stream: TextIO, + has_iterparse: HasIterparse = xml.etree.ElementTree +) -> our_types.PlainMarker: + """ + Read an instance of :py:class:`.types.PlainMarker` from + the :paramref:`stream`. + + Example usage: + + .. code-block:: + + import dummy.xmlization as our_xmlization + + with open_some_stream_over_network(...) as stream: + instance = our_xmlization.plain_marker_from_stream( + stream + ) + + # Do something with the ``instance`` + + :param stream: + representing an instance of + :py:class:`.types.PlainMarker` in XML + :param has_iterparse: + Module containing ``iterparse`` function. + + Default is to use :py:mod:`xml.etree.ElementTree` from the standard + library. If you have to deal with malicious input, consider using + a library such as `defusedxml.ElementTree`_. + :raise: :py:class:`DeserializationException` if unexpected input + :return: + Instance of :py:class:`.types.PlainMarker` read from + :paramref:`stream` + """ + iterator = has_iterparse.iterparse( + stream, + ['start', 'end'] + ) + return plain_marker_from_iterparse( + _with_elements_cleared_after_yield(iterator) + ) + + +def plain_marker_from_file( + path: PathLike, + has_iterparse: HasIterparse = xml.etree.ElementTree +) -> our_types.PlainMarker: + """ + Read an instance of :py:class:`.types.PlainMarker` from + the :paramref:`path`. + + Example usage: + + .. code-block:: + + import pathlib + import dummy.xmlization as our_xmlization + + path = pathlib.Path(...) + instance = our_xmlization.plain_marker_from_file( + path + ) + + # Do something with the ``instance`` + + :param path: + to the file representing an instance of + :py:class:`.types.PlainMarker` in XML + :param has_iterparse: + Module containing ``iterparse`` function. + + Default is to use :py:mod:`xml.etree.ElementTree` from the standard + library. If you have to deal with malicious input, consider using + a library such as `defusedxml.ElementTree`_. + :raise: :py:class:`DeserializationException` if unexpected input + :return: + Instance of :py:class:`.types.PlainMarker` read from + :paramref:`path` + """ + with open(os.fspath(path), "rt", encoding='utf-8') as fid: + iterator = has_iterparse.iterparse( + fid, + ['start', 'end'] + ) + return plain_marker_from_iterparse( + _with_elements_cleared_after_yield(iterator) + ) + + +def plain_marker_from_str( + text: str, + has_iterparse: HasIterparse = xml.etree.ElementTree +) -> our_types.PlainMarker: + """ + Read an instance of :py:class:`.types.PlainMarker` from + the :paramref:`text`. + + Example usage: + + .. code-block:: + + import pathlib + import dummy.xmlization as our_xmlization + + text = "<...>..." + instance = our_xmlization.plain_marker_from_str( + text + ) + + # Do something with the ``instance`` + + :param text: + representing an instance of + :py:class:`.types.PlainMarker` in XML + :param has_iterparse: + Module containing ``iterparse`` function. + + Default is to use :py:mod:`xml.etree.ElementTree` from the standard + library. If you have to deal with malicious input, consider using + a library such as `defusedxml.ElementTree`_. + :raise: :py:class:`DeserializationException` if unexpected input + :return: + Instance of :py:class:`.types.PlainMarker` read from + :paramref:`text` + """ + iterator = has_iterparse.iterparse( + io.StringIO(text), + ['start', 'end'] + ) + return plain_marker_from_iterparse( + _with_elements_cleared_after_yield(iterator) + ) + + def something_from_iterparse( iterator: Iterator[Tuple[str, Element]] ) -> our_types.Something: @@ -2037,6 +2395,82 @@ def _read_blossom_as_element( ) +def _read_marker_as_element( + element: Element, + iterator: Iterator[Tuple[str, Element]] +) -> our_types.Marker: + """ + Read an instance of :py:class:`.types.Marker` from + :paramref:`iterator`, including the end element. + + :param element: start element + :param iterator: + Input stream of ``(event, element)`` coming from + :py:func:`xml.etree.ElementTree.iterparse` with the argument + ``events=["start", "end"]`` + :raise: :py:class:`DeserializationException` if unexpected input + :return: parsed instance + """ + return _read_dispatched( + element, + iterator, + _DISPATCH_FOR_MARKER, + "a concrete instance of 'Marker'" + ) + + +def _read_plain_marker_as_sequence( + element: Element, + iterator: Iterator[Tuple[str, Element]] +) -> our_types.PlainMarker: + """ + Read an instance of :py:class:`.types.PlainMarker` + as a sequence of XML-encoded properties. + + The end element corresponding to the :paramref:`element` will be + read as well. + + :param element: start element, parent of the sequence + :param iterator: + Input stream of ``(event, element)`` coming from + :py:func:`xml.etree.ElementTree.iterparse` with the argument + ``events=["start", "end"]`` + :raise: :py:class:`DeserializationException` if unexpected input + :return: parsed instance + """ + _read_properties( + element, + iterator, + _READERS_FOR_PLAIN_MARKER + ) + + return our_types.PlainMarker() + + +def _read_plain_marker_as_element( + element: Element, + iterator: Iterator[Tuple[str, Element]] +) -> our_types.PlainMarker: + """ + Read an instance of :py:class:`.types.PlainMarker` from + :paramref:`iterator`, including the end element. + + :param element: start element + :param iterator: + Input stream of ``(event, element)`` coming from + :py:func:`xml.etree.ElementTree.iterparse` with the argument + ``events=["start", "end"]`` + :raise: :py:class:`DeserializationException` if unexpected input + :return: parsed instance + """ + return _read_named_element( + element, + iterator, + 'plainMarker', + _read_plain_marker_as_sequence + ) + + def _read_something_as_sequence( element: Element, iterator: Iterator[Tuple[str, Element]] @@ -2251,6 +2685,22 @@ def _read_as_element( } +#: Dispatch XML class names to read-as-sequence functions +#: corresponding to concrete descendants of Marker +_DISPATCH_FOR_MARKER: Mapping[ + str, + Callable[ + [ + Element, + Iterator[Tuple[str, Element]] + ], + our_types.Marker + ] +] = { + 'plainMarker': _read_plain_marker_as_sequence, +} + + #: Dispatch XML class names to read-as-sequence functions #: corresponding to the concrete classes _GENERAL_DISPATCH: Mapping[ @@ -2266,6 +2716,7 @@ def _read_as_element( 'branch': _read_branch_as_sequence, 'leaf': _read_leaf_as_sequence, 'blossom': _read_blossom_as_sequence, + 'plainMarker': _read_plain_marker_as_sequence, 'something': _read_something_as_sequence, 'container': _read_container_as_sequence, } @@ -2307,6 +2758,15 @@ def _read_as_element( } +#: Read the content of a property of +#: :py:class:`.types.PlainMarker`, by the XML name of the property +_READERS_FOR_PLAIN_MARKER: Mapping[ + str, + _ContentReader[Any] +] = { +} + + #: Read the content of a property of #: :py:class:`.types.Something`, by the XML name of the property _READERS_FOR_SOMETHING: Mapping[ @@ -2605,6 +3065,32 @@ def _write_blossom_as_element( _attribute_to_property(exception, prop_name) +def _write_plain_marker_as_element( + name: str, + prop_name: Optional[str], + that: our_types.PlainMarker, + serializer: '_Serializer' +) -> None: + """ + Write :paramref:`that` enclosed in the :paramref:`name` element. + + There are no properties specified for this class, so the element is always + empty. + + :param name: of the element tag. Expected to contain no XML special characters. + :param prop_name: + name of the property, as spelled in Python, whose value is written, or + ``None`` if the access to the value is recorded by an enclosing writer + :param that: instance to be serialized + :param serializer: to write to + :raise: :py:class:`SerializationException` if the value could not be written + """ + try: + serializer.writer.write_empty_element(name) + except Exception as exception: + _attribute_to_property(exception, prop_name) + + def _write_something_as_element( name: str, prop_name: Optional[str], @@ -2726,6 +3212,20 @@ def visit_blossom( """ _write_blossom_as_element('blossom', None, that, self) + def visit_plain_marker( + self, + that: our_types.PlainMarker + ) -> None: + """ + Serialize :paramref:`that` to :py:attr:`~stream` as an XML element. + + The enclosing XML element designates the class of the instance, where its + children correspond to the properties of the instance. + + :param that: instance to be serialized + """ + _write_plain_marker_as_element('plainMarker', None, that, self) + def visit_something( self, that: our_types.Something diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/jsonization.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/jsonization.ts index 1288cd597..f10829d16 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/jsonization.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/jsonization.ts @@ -1065,6 +1065,121 @@ export function blossomFromJsonable( return parsePropertiesOfBlossom(jsonObject); } +/** + * Parse `jsonable` as an instance + * of {@link types!IMarker}. + * + * @param jsonable - to be parsed + * @returns parsed instance, or error if `jsonable` is invalid + */ +export function markerFromJsonable( + jsonable: JsonValue +): OurCommon.Either< + OurTypes.IMarker, + DeserializationError +> { + const objectError = checkIsJsonObject(jsonable); + if (objectError !== null) { + return new OurCommon.Either< + OurTypes.IMarker, + DeserializationError + >( + null, + objectError + ); + } + const jsonObject = jsonable; + + const modelTypeOrError = extractModelType(jsonObject); + if (modelTypeOrError.error !== null) { + return new OurCommon.Either< + OurTypes.IMarker, + DeserializationError + >( + null, + modelTypeOrError.error + ); + } + + const modelType = modelTypeOrError.mustValue(); + + switch (modelType) { + case "PlainMarker": + return parsePropertiesOfPlainMarker(jsonObject); + + default: + return newDeserializationError( + `Unexpected model type for IMarker: ${modelType}` + ); + } +} + +/** + * Parse the properties of an instance + * of {@link types!PlainMarker} from `jsonObject`. + * + * The `modelType` is expected to have been already verified by the caller, + * and is therefore skipped here. + * + * @param jsonObject - JSON object to be parsed + * @returns parsed instance of {@link types!PlainMarker}, + * or an error if any + */ +function parsePropertiesOfPlainMarker( + jsonObject: JsonObject +): OurCommon.Either< + OurTypes.PlainMarker, + DeserializationError +> { + return new OurCommon.Either< + OurTypes.PlainMarker, + DeserializationError + >( + new OurTypes.PlainMarker(), + null + ); +} + +/** + * Parse an instance of {@link types!PlainMarker} from the JSON-able + * structure `jsonable`. + * + * @param jsonable - structure to be parsed + * @returns parsed instance of {@link types!PlainMarker}, + * or an error if any + */ +export function plainMarkerFromJsonable( + jsonable: JsonValue +): OurCommon.Either< + OurTypes.PlainMarker, + DeserializationError +> { + const objectError = checkIsJsonObject(jsonable); + if (objectError !== null) { + return new OurCommon.Either< + OurTypes.PlainMarker, + DeserializationError + >( + null, + objectError + ); + } + const jsonObject = jsonable; + + const modelTypeError = checkModelType(jsonObject, "PlainMarker"); + if (modelTypeError !== null) { + return new OurCommon.Either< + OurTypes.PlainMarker, + DeserializationError + >( + null, + modelTypeError + ); + } + + return parsePropertiesOfPlainMarker(jsonObject); +} + /** * Parse the properties of an instance * of {@link types!Something} from `jsonObject`. @@ -1493,6 +1608,22 @@ function serializeBlossom( return jsonable; } +/** + * Serialize `that` to a JSON-able representation. + * + * @param that - instance to be serialized + * @returns JSON-able representation + */ +function serializePlainMarker( + that: OurTypes.PlainMarker +): JsonObject { + const jsonable: JsonObject = {}; + + jsonable["modelType"] = "PlainMarker"; + + return jsonable; +} + /** * Serialize `that` to a JSON-able representation. * @@ -1577,6 +1708,12 @@ class Serializer extends OurTypes.AbstractTransformer { return serializeBlossom(that); } + transformPlainMarker( + that: OurTypes.PlainMarker + ): JsonObject { + return serializePlainMarker(that); + } + transformSomething( that: OurTypes.Something ): JsonObject { diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/stringification.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/stringification.ts index 1db7ae760..2cb1d1cb1 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/stringification.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/stringification.ts @@ -20,6 +20,10 @@ const MODEL_TYPE_FROM_STRING = new Map([ "Blossom", OurTypes.ModelType.Blossom ], + [ + "PlainMarker", + OurTypes.ModelType.PlainMarker + ], [ "Something", OurTypes.ModelType.Something @@ -50,6 +54,7 @@ const MODEL_TYPE_TO_STRING: readonly string[] = [ "Branch", "Leaf", "Blossom", + "PlainMarker", "Something", "Container" ]; diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/types.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/types.ts index 67392ff71..1450605e6 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/types.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/types.ts @@ -8,8 +8,9 @@ export enum ModelType { Branch = 0, Leaf = 1, Blossom = 2, - Something = 3, - Container = 4 + PlainMarker = 3, + Something = 4, + Container = 5 } /** @@ -29,8 +30,9 @@ export function *overModelType ( yield 0; // Branch yield 1; // Leaf yield 2; // Blossom - yield 3; // Something - yield 4; // Container + yield 3; // PlainMarker + yield 4; // Something + yield 5; // Container } /** @@ -417,6 +419,97 @@ export class Blossom } } +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IMarker extends Class { + // Intentionally empty. +} + +export class PlainMarker + extends Class + implements IMarker { + /** + * Indicate the runtime model type of the instance. + */ + modelType(): ModelType { + // NOTE (mristin): + // We yield numbers instead of literals to avoid name lookups on platforms + // which do not provide JIT compilation of hot paths. + return 3; // PlainMarker + } + + /** + * Iterate over the instances referenced from this instance. + * + * We do not recurse into the referenced instances. + * + * @returns Iterator over the referenced instances + */ + *descendOnce(): IterableIterator { + // No descendable properties + } + + /** + * Iterate recursively over the instances referenced from this instance. + * + * @returns Iterator over the referenced instances + */ + *descend(): IterableIterator { + // No descendable properties + } + + /** + * Dispatch `visitor` on this instance. + * + * @param visitor - to visit this instance + */ + accept(visitor: AbstractVisitor): void { + visitor.visitPlainMarker(this); + } + + /** + * Dispatch `visitor` with `context` on this instance. + * + * @param visitor - to visit this instance + * @param context - to be passed along to the dispatched visitor method + * @typeParam ContextT - type of the context + */ + acceptWithContext( + visitor: AbstractVisitorWithContext, + context: ContextT + ) { + visitor.visitPlainMarkerWithContext(this, context); + } + + /** + * Dispatch the `transformer` on this instance. + * + * @param transformer - to transform this instance + * @returns transformation of this instance + * @paramType T - type of the transformation result + */ + transform(transformer: AbstractTransformer): T { + return transformer.transformPlainMarker(this); + } + + /** + * Dispatch the `transformer` on this instance in `context`. + * + * @param transformer - to transform this instance + * @param context - to be passed along to the `transformer` + * @returns transformation of this instance + * @paramType T - type of the transformation result + * @paramType ContextT - type of the transformation context + */ + transformWithContext( + transformer: AbstractTransformerWithContext, + context: ContextT + ): T { + return transformer.transformPlainMarkerWithContext( + this, context + ); + } +} + export class Something extends Class { /** * Indicate the runtime model type of the instance. @@ -425,7 +518,7 @@ export class Something extends Class { // NOTE (mristin): // We yield numbers instead of literals to avoid name lookups on platforms // which do not provide JIT compilation of hot paths. - return 3; // Something + return 4; // Something } someChoice: INode; @@ -543,7 +636,7 @@ export class Container extends Class { // NOTE (mristin): // We yield numbers instead of literals to avoid name lookups on platforms // which do not provide JIT compilation of hot paths. - return 4; // Container + return 5; // Container } node: INode; @@ -691,6 +784,15 @@ export abstract class AbstractVisitor { that: Blossom ): void; + /** + * Visit `that`. + * + * @param that - instance to be visited + */ + abstract visitPlainMarker( + that: PlainMarker + ): void; + /** * Visit `that`. * @@ -762,6 +864,17 @@ export abstract class AbstractVisitorWithContext { context: ContextT ): void; + /** + * Visit `that` in `context`. + * + * @param that - instance to be visited + * @param context - of the visitation + */ + abstract visitPlainMarkerWithContext( + that: PlainMarker, + context: ContextT + ): void; + /** * Visit `that` in `context`. * @@ -832,6 +945,19 @@ export class PassThroughVisitor extends AbstractVisitor { } } + /** + * Visit `that`. + * + * @param that - instance to be visited + */ + visitPlainMarker( + that: PlainMarker + ): void { + for (const another of that.descendOnce()) { + this.visit(another); + } + } + /** * Visit `that`. * @@ -923,6 +1049,21 @@ export class PassThroughVisitorWithContext } } + /** + * Visit `that` in `context`. + * + * @param that - instance to be visited + * @param context - of the visitation + */ + visitPlainMarkerWithContext( + that: PlainMarker, + context: ContextT + ): void { + for (const another of that.descendOnce()) { + this.visitWithContext(another, context); + } + } + /** * Visit `that` in `context`. * @@ -997,6 +1138,16 @@ export abstract class AbstractTransformer { that: Blossom ): T; + /** + * Transform `that`. + * + * @param that - instance to be transformed + * @returns transformed `that` + */ + abstract transformPlainMarker( + that: PlainMarker + ): T; + /** * Transform `that`. * @@ -1075,6 +1226,18 @@ export abstract class AbstractTransformerWithContext { context: ContextT ): T; + /** + * Transform `that` in `context`. + * + * @param that - instance to be transformed + * @param context - of the transformation + * @returns transformed `that` + */ + abstract transformPlainMarkerWithContext( + that: PlainMarker, + context: ContextT + ): T; + /** * Transform `that` in `context`. * @@ -1167,6 +1330,20 @@ export class TransformerWithDefault extends AbstractTransformer { } /* eslint-enable @typescript-eslint/no-unused-vars */ + /** + * Transform `that`. + * + * @param that - instance to be transformed + * @returns transformed `that` + */ + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformPlainMarker( + that: PlainMarker + ): T { + return this.defaultResult; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + /** * Transform `that`. * @@ -1271,6 +1448,22 @@ export class TransformerWithDefaultAndContext } /* eslint-enable @typescript-eslint/no-unused-vars */ + /** + * Transform `that` in `context`. + * + * @param that - instance to be transformed + * @param context - of the visitation + * @returns transformed `that` + */ + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformPlainMarkerWithContext( + that: PlainMarker, + context: ContextT + ): T { + return this.defaultResult; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + /** * Transform `that` in `context`. * @@ -1327,6 +1520,14 @@ class AsNodeTransformer return that as INode; } + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformPlainMarker( + that: PlainMarker + ): INode | null { + return null; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ transformSomething( that: Something @@ -1395,6 +1596,14 @@ class AsBranchTransformer return that as IBranch; } + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformPlainMarker( + that: PlainMarker + ): IBranch | null { + return null; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ transformSomething( that: Something @@ -1465,6 +1674,14 @@ class AsLeafTransformer return that as ILeaf; } + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformPlainMarker( + that: PlainMarker + ): ILeaf | null { + return null; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ transformSomething( that: Something @@ -1537,6 +1754,113 @@ export function isBlossom( return that instanceof Blossom; } +/** + * Try to cast an instance of the model to {@link IMarker}. + */ +class AsMarkerTransformer + extends AbstractTransformer { + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformBranch( + that: Branch + ): IMarker | null { + return null; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformLeaf( + that: Leaf + ): IMarker | null { + return null; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformBlossom( + that: Blossom + ): IMarker | null { + return null; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + + transformPlainMarker( + that: PlainMarker + ): IMarker | null { + return that as IMarker; + } + + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformSomething( + that: Something + ): IMarker | null { + return null; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformContainer( + that: Container + ): IMarker | null { + return null; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ +} + +const AS_MARKER_TRANSFORMER = + new AsMarkerTransformer(); + +/** + * Try to cast `that` instance to + * the interface {@link IMarker}. + * + * @param that - instance to be casted + * @returns - casted `that` if cast successful, or `null` + */ +export function asMarker( + that: Class +): IMarker | null { + return AS_MARKER_TRANSFORMER.transform(that); +} + +/** + * Check the type of `that` instance. + * + * @param that - instance to be type-checked + * @returns `true` if the type check is successful + */ +export function isMarker( + that: Class +): that is IMarker { + return asMarker(that) !== null; +} + +/** + * Try to cast `that` instance to + * the class {@link PlainMarker}. + * + * @param that - instance to be casted + * @returns - casted `that` if cast successful, or `null` + */ +export function asPlainMarker( + that: Class +): PlainMarker | null { + return (that instanceof PlainMarker) + ? that + : null; +} + +/** + * Check the type of `that` instance. + * + * @param that - instance to be type-checked + * @returns `true` if the type check is successful + */ +export function isPlainMarker( + that: Class +): that is PlainMarker { + return that instanceof PlainMarker; +} + /** * Try to cast `that` instance to * the class {@link Something}. @@ -1622,6 +1946,15 @@ class TypeMatcher extends AbstractTransformerWithContext< } /* eslint-enable @typescript-eslint/no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ + transformPlainMarkerWithContext( + that: PlainMarker, + other: Class + ): boolean { + return isPlainMarker(other); + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ transformSomethingWithContext( that: Something, diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/verification.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/verification.ts index b468ecdb6..f76e2d172 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/verification.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/verification.ts @@ -223,6 +223,15 @@ class Verifier // No verification has been defined for Blossom. } + *transformPlainMarkerWithContext( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + that: OurTypes.PlainMarker, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + context: boolean + ): IterableIterator { + // No verification has been defined for PlainMarker. + } + *transformSomethingWithContext( that: OurTypes.Something, context: boolean diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/xmlization.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/xmlization.ts index 58ca2255c..b9295e71f 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/xmlization.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/xmlization.ts @@ -639,6 +639,69 @@ function parseBlossomFromSequence( ); } +/** + * Parse the sequence of properties of an instance + * of {@link types!PlainMarker}. + * + * The opening tag is expected to have been already read by the caller, and + * the caller is expected to read and verify the corresponding closing tag + * after this function returns successfully. This is the contract of + * a `ContentParser`, so this function is used as one wherever an instance + * of {@link types!PlainMarker} is embedded. + */ +function parsePlainMarkerFromSequence( + cursor: XmlCursor +): OurCommon.Either { + // No properties + + const className = OurTypes.PlainMarker.name; + + cursor.skipIgnorable(); + // eslint-disable-next-line no-constant-condition + while (true) { + const nextTagOrError = nextPropertyOpenTag(cursor, className); + if (nextTagOrError === null) { + break; + } + if (nextTagOrError instanceof DeserializationError) { + return new OurCommon.Either( + null, + nextTagOrError + ); + } + + const propertyLocalName = localNameOfTag(nextTagOrError.tag); + + let propertyError: DeserializationError | null = null; + switch (propertyLocalName) { + default: { + propertyError = new DeserializationError( + `Unexpected XML property: ${propertyLocalName}` + ); + break; + } + } + + if (propertyError !== null) { + propertyError.path.prepend(new ElementSegment(propertyLocalName)); + return new OurCommon.Either( + null, + propertyError + ); + } + + cursor.skipIgnorable(); + } + + // No required properties + + const instance = new OurTypes.PlainMarker(); + return new OurCommon.Either( + instance, + null + ); +} + /** * Parse the sequence of properties of an instance * of {@link types!Something}. @@ -947,6 +1010,19 @@ function writeBlossomAsSequence( ); } +/** + * Write the properties of an instance + * of {@link types!PlainMarker}, and neither the opening + * nor the closing tag of the element which holds them -- which is the contract of + * a `ContentWriter`, so this function is used as one. + */ +function writePlainMarkerAsSequence( + parts: Array, + that: OurTypes.PlainMarker +): void { + // No properties +} + /** * Write the properties of an instance * of {@link types!Something}, and neither the opening @@ -1201,6 +1277,73 @@ export function leafFromXmlString( return instanceOrError; } +const PARSERS_OF_MARKER = new Map< + string, + ContentParser +>([ + ["plainMarker", parsePlainMarkerFromSequence] +]); + +/** + * Dispatch-parse an instance + * of {@link types!IMarker} from the next + * XML element in `cursor`, based on the element's local name. + * + * @param cursor - to read from + * @returns the parsed instance, or an error + */ +function dispatchParseMarkerElement( + cursor: XmlCursor +): OurCommon.Either { + return dispatchParseElement( + cursor, + "IMarker", + PARSERS_OF_MARKER + ); +} + +/** + * Parse an XML string as an instance + * of {@link types!IMarker}. + * + * @param xml - XML string to parse + * @returns parsed instance, or an error + */ +export function markerFromXmlString( + xml: string +): OurCommon.Either { + if (xml.length === 0) { + return newDeserializationError( + "Expected an XML document, but got an empty string" + ); + } + + const tokensOrError = tokenizeXml(xml); + if (tokensOrError.error !== null) { + return new OurCommon.Either( + null, + tokensOrError.error + ); + } + + const cursor = new XmlCursor(tokensOrError.mustValue()); + + const instanceOrError = dispatchParseMarkerElement(cursor); + if (instanceOrError.error !== null) { + return instanceOrError; + } + + cursor.skipIgnorable(); + if (cursor.current() !== null) { + return newDeserializationError( + "Expected no tokens after the root XML element, but got token kind: " + + currentTokenKind(cursor) + ); + } + + return instanceOrError; +} + const ROOT_DISPATCH_BY_LOCAL_NAME = new Map< string, ContentParser @@ -1208,6 +1351,7 @@ const ROOT_DISPATCH_BY_LOCAL_NAME = new Map< ["branch", parseBranchFromSequence], ["leaf", parseLeafFromSequence], ["blossom", parseBlossomFromSequence], + ["plainMarker", parsePlainMarkerFromSequence], ["something", parseSomethingFromSequence], ["container", parseContainerFromSequence] ]); @@ -1443,6 +1587,18 @@ class Serializer extends OurTypes.AbstractVisitorWithContext> { ); } + visitPlainMarkerWithContext( + that: OurTypes.PlainMarker, + parts: Array + ): void { + writeElement( + parts, + "plainMarker", + that, + writePlainMarkerAsSequence + ); + } + visitSomethingWithContext( that: OurTypes.Something, parts: Array diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/commonJsonization.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/commonJsonization.ts index 3352bfbff..6f4a95fa5 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/commonJsonization.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/commonJsonization.ts @@ -204,6 +204,70 @@ export function loadMinimalBlossom( return casted; } +/** + * Load a maximal example of {@link types.PlainMarker} from + * the test data directory. + */ +export function loadMaximalPlainMarker( +): OurTypes.PlainMarker { + const aPath = path.join( + TestCommon.TEST_DATA_DIR, + "Json", + "Expected", + "PlainMarker", + "maximal.json" + ); + + const jsonable = TestCommon.readJsonFromFileSync(aPath); + + const instanceOrError = OurJsonization.plainMarkerFromJsonable( + jsonable + ); + expect(instanceOrError.error).toBeNull(); + const instance = instanceOrError.mustValue(); + + const casted = OurTypes.asPlainMarker(instance); + if (casted === null) { + throw new Error( + `Expected instance of PlainMarker in ${aPath}, ` + + `but got: ${typeof instance}` + ); + } + return casted; +} + +/** + * Load a minimal example of {@link types.PlainMarker} from + * the test data directory. + */ +export function loadMinimalPlainMarker( +): OurTypes.PlainMarker { + const aPath = path.join( + TestCommon.TEST_DATA_DIR, + "Json", + "Expected", + "PlainMarker", + "minimal.json" + ); + + const jsonable = TestCommon.readJsonFromFileSync(aPath); + + const instanceOrError = OurJsonization.plainMarkerFromJsonable( + jsonable + ); + expect(instanceOrError.error).toBeNull(); + const instance = instanceOrError.mustValue(); + + const casted = OurTypes.asPlainMarker(instance); + if (casted === null) { + throw new Error( + `Expected instance of PlainMarker in ${aPath}, ` + + `but got: ${typeof instance}` + ); + } + return casted; +} + /** * Load a maximal example of {@link types.Something} from * the test data directory. diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/commonXmlization.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/commonXmlization.ts index d423ea118..d362306e1 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/commonXmlization.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/commonXmlization.ts @@ -205,6 +205,70 @@ export function loadMinimalBlossom( return casted; } +/** + * Load a maximal XML example of {@link types.PlainMarker} + * from the test data directory. + */ +export function loadMaximalPlainMarker( +): OurTypes.PlainMarker { + const aPath = path.join( + TestCommon.TEST_DATA_DIR, + "Xml", + "Expected", + "plainMarker", + "maximal.xml" + ); + + const text = fs.readFileSync(aPath, "utf-8"); + + const instanceOrError = OurXmlization.fromXmlString( + text + ); + expect(instanceOrError.error).toBeNull(); + const instance = instanceOrError.mustValue(); + + const casted = OurTypes.asPlainMarker(instance); + if (casted === null) { + throw new Error( + `Expected instance of PlainMarker in ${aPath}, ` + + `but got: ${typeof instance}` + ); + } + return casted; +} + +/** + * Load a minimal XML example of {@link types.PlainMarker} + * from the test data directory. + */ +export function loadMinimalPlainMarker( +): OurTypes.PlainMarker { + const aPath = path.join( + TestCommon.TEST_DATA_DIR, + "Xml", + "Expected", + "plainMarker", + "minimal.xml" + ); + + const text = fs.readFileSync(aPath, "utf-8"); + + const instanceOrError = OurXmlization.fromXmlString( + text + ); + expect(instanceOrError.error).toBeNull(); + const instance = instanceOrError.mustValue(); + + const casted = OurTypes.asPlainMarker(instance); + if (casted === null) { + throw new Error( + `Expected instance of PlainMarker in ${aPath}, ` + + `but got: ${typeof instance}` + ); + } + return casted; +} + /** * Load a maximal XML example of {@link types.Something} * from the test data directory. diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.common.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.common.spec.ts index 705d9a99b..05d9f996e 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.common.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.common.spec.ts @@ -22,6 +22,11 @@ test("commonJsonization loaders for Blossom", () => { expect(TestCommonJsonization.loadMaximalBlossom()).not.toBeNull(); }); +test("commonJsonization loaders for PlainMarker", () => { + expect(TestCommonJsonization.loadMinimalPlainMarker()).not.toBeNull(); + expect(TestCommonJsonization.loadMaximalPlainMarker()).not.toBeNull(); +}); + test("commonJsonization loaders for Something", () => { expect(TestCommonJsonization.loadMinimalSomething()).not.toBeNull(); expect(TestCommonJsonization.loadMaximalSomething()).not.toBeNull(); diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.concreteClasses.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.concreteClasses.spec.ts index 283a9a8dd..1a8161b6c 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.concreteClasses.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.concreteClasses.spec.ts @@ -562,6 +562,149 @@ test("Blossom verification fail", () => { } }); +test("PlainMarker round-trip OK", () => { + const pths = Array.from( + TestCommon.findFilesBySuffixRecursively( + path.join( + TestCommon.TEST_DATA_DIR, + "Json", + "Expected", + "PlainMarker" + ), + ".json" + ) + ); + pths.sort(); + + for (const pth of pths) { + const jsonable = TestCommon.readJsonFromFileSync(pth); + + const instanceOrError = OurJsonization.plainMarkerFromJsonable( + jsonable + ); + expect(instanceOrError.error).toBeNull(); + const instance = instanceOrError.mustValue(); + + TestCommon.assertNoVerificationErrors(OurVerification.verify(instance), pth); + + assertSerializeDeserializeEqualsOriginal( + jsonable, + instance, + pth + ); + } +}); + +test("PlainMarker deserialization fail", () => { + for ( + const causeDir of + TestCommon.findImmediateSubdirectories( + path.join( + TestCommon.TEST_DATA_DIR, + "Json", + "Unexpected", + "Unserializable" + ) + ) + ) { + // NOTE (mristin): + // Unlike other SDKs, we can not be really sure what additional properties + // JavaScript might bring about. Therefore, we leave out the tests with + // the validation of additional properties. + if (path.basename(causeDir) == "UnexpectedAdditionalProperty") { + continue; + } + + const clsDir = path.join( + causeDir, + "PlainMarker" + ); + if (!fs.existsSync(clsDir)) { + // NOTE (mristin): + // Some classes indeed lack the invalid examples. + continue; + } + + const pths = Array.from( + TestCommon.findFilesBySuffixRecursively( + clsDir, + ".json" + ) + ); + pths.sort(); + + for (const pth of pths) { + const jsonable = TestCommon.readJsonFromFileSync(pth); + + const instanceOrError = OurJsonization.plainMarkerFromJsonable( + jsonable + ); + if (instanceOrError.error === null) { + throw new Error(`Expected a de-serialization error for ${pth}, but got none`); + } + + assertDeserializationErrorEqualsExpectedOrRecord( + instanceOrError.error, + pth + ); + } + } +}); + +test("PlainMarker verification fail", () => { + for ( + const causeDir of + TestCommon.findImmediateSubdirectories( + path.join( + TestCommon.TEST_DATA_DIR, + "Json", + "Unexpected", + "Invalid" + ) + ) + ) { + const clsDir = path.join( + causeDir, + "PlainMarker" + ); + if (!fs.existsSync(clsDir)) { + // NOTE (mristin): + // Some classes indeed lack the invalid examples. + continue; + } + + const pths = Array.from( + TestCommon.findFilesBySuffixRecursively( + clsDir, + ".json" + ) + ); + pths.sort(); + + for (const pth of pths) { + const jsonable = TestCommon.readJsonFromFileSync(pth); + + const instanceOrError = OurJsonization.plainMarkerFromJsonable( + jsonable + ); + if (instanceOrError.error !== null) { + throw new Error( + `Expected no de-serialization error for ${pth}, ` + + `but got: ${instanceOrError.error.message}: ${instanceOrError.error.path}` + ); + } + + const instance = instanceOrError.mustValue(); + + const verificationErrors = Array.from(OurVerification.verify(instance)); + assertVerificationErrorsEqualExpectedOrRecord( + verificationErrors, + pth + ); + } + } +}); + test("Something round-trip OK", () => { const pths = Array.from( TestCommon.findFilesBySuffixRecursively( diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.interfaces.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.interfaces.spec.ts index 81c52f26b..090c398af 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.interfaces.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/jsonization.interfaces.spec.ts @@ -282,5 +282,46 @@ test("ILeaf deserialization fail", () => { ); }); +test( + "IMarker round-trip " + + "starting from PlainMarker OK", + () => { + const instance = TestCommonJsonization.loadMinimalPlainMarker(); + + const jsonable = OurJsonization.toJsonable(instance); + + const anotherInstanceOrError = OurJsonization.markerFromJsonable( + jsonable + ); + expect(anotherInstanceOrError.error).toBeNull(); + const anotherInstance = anotherInstanceOrError.mustValue(); + + const anotherJsonable = OurJsonization.toJsonable(anotherInstance); + + const inequalityError = TestCommon.checkJsonablesEqual( + jsonable, + anotherJsonable + ); + if (inequalityError !== null) { + throw new Error( + "The minimal example of IMarker " + + "as an instance of PlainMarker serialized " + + "to JSON, then de-serialized and serialized again does not match " + + `the first JSON: ${inequalityError.path}: ${inequalityError.message}` + ); + } +}); + +test("IMarker deserialization fail", () => { + const jsonable = "This is not a IMarker."; + + const instanceOrError = OurJsonization.markerFromJsonable( + jsonable + ); + expect(instanceOrError.error.message).toStrictEqual( + "Expected a JSON object, but got: string" + ); +}); + // This code has been automatically generated by aas-core-codegen. // Do NOT edit or append. diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.casts.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.casts.spec.ts index 0bc9b514e..17f4f2392 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.casts.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.casts.spec.ts @@ -40,6 +40,20 @@ test("casts over an instance of Branch", () => { OurTypes.asBlossom(theBranch) ).toBeNull(); + expect( + OurTypes.isMarker(theBranch) + ).toStrictEqual(false); + expect( + OurTypes.asMarker(theBranch) + ).toBeNull(); + + expect( + OurTypes.isPlainMarker(theBranch) + ).toStrictEqual(false); + expect( + OurTypes.asPlainMarker(theBranch) + ).toBeNull(); + expect( OurTypes.isSomething(theBranch) ).toStrictEqual(false); @@ -87,6 +101,20 @@ test("casts over an instance of Leaf", () => { OurTypes.asBlossom(theLeaf) ).toBeNull(); + expect( + OurTypes.isMarker(theLeaf) + ).toStrictEqual(false); + expect( + OurTypes.asMarker(theLeaf) + ).toBeNull(); + + expect( + OurTypes.isPlainMarker(theLeaf) + ).toStrictEqual(false); + expect( + OurTypes.asPlainMarker(theLeaf) + ).toBeNull(); + expect( OurTypes.isSomething(theLeaf) ).toStrictEqual(false); @@ -134,6 +162,20 @@ test("casts over an instance of Blossom", () => { OurTypes.asBlossom(theBlossom) ).toStrictEqual(theBlossom); + expect( + OurTypes.isMarker(theBlossom) + ).toStrictEqual(false); + expect( + OurTypes.asMarker(theBlossom) + ).toBeNull(); + + expect( + OurTypes.isPlainMarker(theBlossom) + ).toStrictEqual(false); + expect( + OurTypes.asPlainMarker(theBlossom) + ).toBeNull(); + expect( OurTypes.isSomething(theBlossom) ).toStrictEqual(false); @@ -149,6 +191,67 @@ test("casts over an instance of Blossom", () => { ).toBeNull(); }); +test("casts over an instance of PlainMarker", () => { + const thePlainMarker = + TestCommonJsonization.loadMinimalPlainMarker(); + + expect( + OurTypes.isNode(thePlainMarker) + ).toStrictEqual(false); + expect( + OurTypes.asNode(thePlainMarker) + ).toBeNull(); + + expect( + OurTypes.isBranch(thePlainMarker) + ).toStrictEqual(false); + expect( + OurTypes.asBranch(thePlainMarker) + ).toBeNull(); + + expect( + OurTypes.isLeaf(thePlainMarker) + ).toStrictEqual(false); + expect( + OurTypes.asLeaf(thePlainMarker) + ).toBeNull(); + + expect( + OurTypes.isBlossom(thePlainMarker) + ).toStrictEqual(false); + expect( + OurTypes.asBlossom(thePlainMarker) + ).toBeNull(); + + expect( + OurTypes.isMarker(thePlainMarker) + ).toStrictEqual(true); + expect( + OurTypes.asMarker(thePlainMarker) + ).toStrictEqual(thePlainMarker); + + expect( + OurTypes.isPlainMarker(thePlainMarker) + ).toStrictEqual(true); + expect( + OurTypes.asPlainMarker(thePlainMarker) + ).toStrictEqual(thePlainMarker); + + expect( + OurTypes.isSomething(thePlainMarker) + ).toStrictEqual(false); + expect( + OurTypes.asSomething(thePlainMarker) + ).toBeNull(); + + expect( + OurTypes.isContainer(thePlainMarker) + ).toStrictEqual(false); + expect( + OurTypes.asContainer(thePlainMarker) + ).toBeNull(); +}); + test("casts over an instance of Something", () => { const theSomething = TestCommonJsonization.loadMinimalSomething(); @@ -181,6 +284,20 @@ test("casts over an instance of Something", () => { OurTypes.asBlossom(theSomething) ).toBeNull(); + expect( + OurTypes.isMarker(theSomething) + ).toStrictEqual(false); + expect( + OurTypes.asMarker(theSomething) + ).toBeNull(); + + expect( + OurTypes.isPlainMarker(theSomething) + ).toStrictEqual(false); + expect( + OurTypes.asPlainMarker(theSomething) + ).toBeNull(); + expect( OurTypes.isSomething(theSomething) ).toStrictEqual(true); @@ -228,6 +345,20 @@ test("casts over an instance of Container", () => { OurTypes.asBlossom(theContainer) ).toBeNull(); + expect( + OurTypes.isMarker(theContainer) + ).toStrictEqual(false); + expect( + OurTypes.asMarker(theContainer) + ).toBeNull(); + + expect( + OurTypes.isPlainMarker(theContainer) + ).toStrictEqual(false); + expect( + OurTypes.asPlainMarker(theContainer) + ).toBeNull(); + expect( OurTypes.isSomething(theContainer) ).toStrictEqual(false); diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.descendAndPassThroughVisitor.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.descendAndPassThroughVisitor.spec.ts index 58ce9a272..b787f6874 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.descendAndPassThroughVisitor.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.descendAndPassThroughVisitor.spec.ts @@ -154,6 +154,26 @@ test("descend against PassThroughVisitor", () => { expectDescendAndPassThroughVisitorSame(instance); }); +test("descend of PlainMarker", () => { + const instance = TestCommonJsonization.loadMaximalPlainMarker(); + + compareOrRecordTrace( + instance, + path.join( + TestCommon.TEST_DATA_DIR, + "descend", + "PlainMarker", + "maximal.json.trace" + ) + ); +}); + +test("descend against PassThroughVisitor", () => { + const instance = TestCommonJsonization.loadMaximalPlainMarker(); + + expectDescendAndPassThroughVisitorSame(instance); +}); + test("descend of Something", () => { const instance = TestCommonJsonization.loadMaximalSomething(); diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.descendOnce.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.descendOnce.spec.ts index 98d96c564..17e41f9f0 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.descendOnce.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.descendOnce.spec.ts @@ -97,6 +97,20 @@ test("descendOnce of Blossom", () => { ); }); +test("descendOnce of PlainMarker", () => { + const instance = TestCommonJsonization.loadMaximalPlainMarker(); + + compareOrRecordTrace( + instance, + path.join( + TestCommon.TEST_DATA_DIR, + "descendOnce", + "PlainMarker", + "maximal.json.trace" + ) + ); +}); + test("descendOnce of Something", () => { const instance = TestCommonJsonization.loadMaximalSomething(); diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.modelType.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.modelType.spec.ts index 2d5d26ac2..8f0e2f257 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.modelType.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.modelType.spec.ts @@ -96,6 +96,35 @@ test("model type to string of Blossom", () => { ); }); +test("model type of PlainMarker", () => { + const instance = TestCommonJsonization.loadMinimalPlainMarker(); + + expect(instance.modelType()).toStrictEqual( + OurTypes.ModelType.PlainMarker + ); +}); + +test("model type from string of PlainMarker", () => { + const text = "PlainMarker"; + const literal = OurStringification.modelTypeFromString( + text + ); + + expect(literal).toStrictEqual( + OurTypes.ModelType.PlainMarker + ); +}); + +test("model type to string of PlainMarker", () => { + const text = OurStringification.mustModelTypeToString( + OurTypes.ModelType.PlainMarker + ); + + expect(text).toStrictEqual( + "PlainMarker" + ); +}); + test("model type of Something", () => { const instance = TestCommonJsonization.loadMinimalSomething(); diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.typeMatches.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.typeMatches.spec.ts index 5b740a726..0e8d2a043 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.typeMatches.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/types.typeMatches.spec.ts @@ -14,6 +14,8 @@ const THE_LEAF = TestCommonJsonization.loadMinimalLeaf(); const THE_BLOSSOM = TestCommonJsonization.loadMinimalBlossom(); +const THE_PLAIN_MARKER = TestCommonJsonization.loadMinimalPlainMarker(); + const THE_SOMETHING = TestCommonJsonization.loadMinimalSomething(); const THE_CONTAINER = TestCommonJsonization.loadMinimalContainer(); @@ -40,6 +42,13 @@ test("type matches for Branch", () => { ) ).toStrictEqual(true); + expect( + OurTypes.typesMatch( + THE_BRANCH, + THE_PLAIN_MARKER + ) + ).toStrictEqual(false); + expect( OurTypes.typesMatch( THE_BRANCH, @@ -77,6 +86,13 @@ test("type matches for Leaf", () => { ) ).toStrictEqual(true); + expect( + OurTypes.typesMatch( + THE_LEAF, + THE_PLAIN_MARKER + ) + ).toStrictEqual(false); + expect( OurTypes.typesMatch( THE_LEAF, @@ -114,6 +130,13 @@ test("type matches for Blossom", () => { ) ).toStrictEqual(true); + expect( + OurTypes.typesMatch( + THE_BLOSSOM, + THE_PLAIN_MARKER + ) + ).toStrictEqual(false); + expect( OurTypes.typesMatch( THE_BLOSSOM, @@ -129,6 +152,50 @@ test("type matches for Blossom", () => { ).toStrictEqual(false); }); +test("type matches for PlainMarker", () => { + expect( + OurTypes.typesMatch( + THE_PLAIN_MARKER, + THE_BRANCH + ) + ).toStrictEqual(false); + + expect( + OurTypes.typesMatch( + THE_PLAIN_MARKER, + THE_LEAF + ) + ).toStrictEqual(false); + + expect( + OurTypes.typesMatch( + THE_PLAIN_MARKER, + THE_BLOSSOM + ) + ).toStrictEqual(false); + + expect( + OurTypes.typesMatch( + THE_PLAIN_MARKER, + THE_PLAIN_MARKER + ) + ).toStrictEqual(true); + + expect( + OurTypes.typesMatch( + THE_PLAIN_MARKER, + THE_SOMETHING + ) + ).toStrictEqual(false); + + expect( + OurTypes.typesMatch( + THE_PLAIN_MARKER, + THE_CONTAINER + ) + ).toStrictEqual(false); +}); + test("type matches for Something", () => { expect( OurTypes.typesMatch( @@ -151,6 +218,13 @@ test("type matches for Something", () => { ) ).toStrictEqual(false); + expect( + OurTypes.typesMatch( + THE_SOMETHING, + THE_PLAIN_MARKER + ) + ).toStrictEqual(false); + expect( OurTypes.typesMatch( THE_SOMETHING, @@ -188,6 +262,13 @@ test("type matches for Container", () => { ) ).toStrictEqual(false); + expect( + OurTypes.typesMatch( + THE_CONTAINER, + THE_PLAIN_MARKER + ) + ).toStrictEqual(false); + expect( OurTypes.typesMatch( THE_CONTAINER, diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.common.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.common.spec.ts index c62d1c7f1..a36ec2bd4 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.common.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.common.spec.ts @@ -22,6 +22,11 @@ test("commonXmlization loaders for Blossom", () => { expect(TestCommonXmlization.loadMaximalBlossom()).not.toBeNull(); }); +test("commonXmlization loaders for PlainMarker", () => { + expect(TestCommonXmlization.loadMinimalPlainMarker()).not.toBeNull(); + expect(TestCommonXmlization.loadMaximalPlainMarker()).not.toBeNull(); +}); + test("commonXmlization loaders for Something", () => { expect(TestCommonXmlization.loadMinimalSomething()).not.toBeNull(); expect(TestCommonXmlization.loadMaximalSomething()).not.toBeNull(); diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.concreteClasses.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.concreteClasses.spec.ts index 19edff79d..8f7e08d96 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.concreteClasses.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.concreteClasses.spec.ts @@ -230,6 +230,78 @@ test("Blossom XML deserialization fail", () => { } }); +test("PlainMarker XML round-trip OK", () => { + const pths = Array.from( + TestCommon.findFilesBySuffixRecursively( + path.join( + TestCommon.TEST_DATA_DIR, + "Xml", + "Expected", + "plainMarker" + ), + ".xml" + ) + ); + pths.sort(); + + for (const pth of pths) { + const text = fs.readFileSync(pth, "utf-8"); + + const instanceOrError = OurXmlization.fromXmlString(text); + expect(instanceOrError.error).toBeNull(); + const instance = instanceOrError.mustValue(); + + const casted = OurTypes.asPlainMarker(instance); + if (casted === null) { + throw new Error( + `Expected instance of PlainMarker in ${pth}, ` + + `but got: ${typeof instance}` + ); + } + + TestCommon.assertNoVerificationErrors(OurVerification.verify(casted), pth); + + const roundTripText = OurXmlization.toXmlString(casted); + expect(roundTripText.length).toBeGreaterThan(0); + } +}); + +test("PlainMarker XML deserialization fail", () => { + for ( + const causeDir of + TestCommon.findImmediateSubdirectories( + path.join( + TestCommon.TEST_DATA_DIR, + "Xml", + "Unexpected", + "Unserializable" + ) + ) + ) { + const clsDir = path.join( + causeDir, + "plainMarker" + ); + if (!fs.existsSync(clsDir)) { + continue; + } + + const pths = Array.from( + TestCommon.findFilesBySuffixRecursively( + clsDir, + ".xml" + ) + ); + pths.sort(); + + for (const pth of pths) { + const text = fs.readFileSync(pth, "utf-8"); + const instanceOrError = OurXmlization.fromXmlString(text); + expect(instanceOrError.error).not.toBeNull(); + } + } +}); + test("Something XML round-trip OK", () => { const pths = Array.from( TestCommon.findFilesBySuffixRecursively( diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.interfaces.spec.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.interfaces.spec.ts index 30f82a557..8f76200fd 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.interfaces.spec.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/test/xmlization.interfaces.spec.ts @@ -298,5 +298,51 @@ test( } ); +test( + "IMarker XML round-trip " + + "starting from PlainMarker OK", + () => { + const instance = TestCommonXmlization.loadMinimalPlainMarker(); + + const xmlText = OurXmlization.toXmlString(instance); + const anotherInstanceOrError = OurXmlization.fromXmlString(xmlText); + expect(anotherInstanceOrError.error).toBeNull(); + const anotherInstance = anotherInstanceOrError.mustValue(); + + const asInterface = OurTypes.asMarker(anotherInstance); + expect(asInterface).not.toBeNull(); + } +); + +test( + "IMarker XML round-trip via " + + "markerFromXmlString starting from PlainMarker OK", + () => { + const instance = TestCommonXmlization.loadMinimalPlainMarker(); + + const xmlText = OurXmlization.toXmlString(instance); + const anotherInstanceOrError = OurXmlization.markerFromXmlString( + xmlText + ); + expect(anotherInstanceOrError.error).toBeNull(); + } +); + +test("IMarker XML deserialization fail", () => { + const instanceOrError = OurXmlization.fromXmlString("This is not XML."); + expect(instanceOrError.error).not.toBeNull(); +}); + +test( + "IMarker XML deserialization fail via " + + "markerFromXmlString", + () => { + const instanceOrError = OurXmlization.markerFromXmlString( + "This is not XML." + ); + expect(instanceOrError.error).not.toBeNull(); + } +); + // This code has been automatically generated by aas-core-codegen. // Do NOT edit or append. diff --git a/dev/test_data/main/xsd/expected/deep_class_hierarchy/expected_output/schema.xsd b/dev/test_data/main/xsd/expected/deep_class_hierarchy/expected_output/schema.xsd index f1531fd6b..beaac752e 100644 --- a/dev/test_data/main/xsd/expected/deep_class_hierarchy/expected_output/schema.xsd +++ b/dev/test_data/main/xsd/expected/deep_class_hierarchy/expected_output/schema.xsd @@ -43,6 +43,14 @@ + + + + + + + + @@ -55,6 +63,11 @@ + + + + + @@ -93,11 +106,21 @@ + + + + + + + + + + diff --git a/dev/test_data/parse/common_meta_models/deep_class_hierarchy/expected_symbol_table.txt b/dev/test_data/parse/common_meta_models/deep_class_hierarchy/expected_symbol_table.txt index fd88d51c5..796cb4cb1 100644 --- a/dev/test_data/parse/common_meta_models/deep_class_hierarchy/expected_symbol_table.txt +++ b/dev/test_data/parse/common_meta_models/deep_class_hierarchy/expected_symbol_table.txt @@ -234,6 +234,70 @@ UnverifiedSymbolTable( node=..., properties_by_name=..., methods_by_name=...), + AbstractClass( + name='Marker', + inheritances=[], + properties=[], + methods=[ + ConstructorToBeUnderstood( + name='__init__', + verification=False, + visibility='PUBLIC', + arguments=[ + Argument( + name='self', + type_annotation=SelfTypeAnnotation(), + default=None, + node=...)], + returns=None, + description=None, + contracts=Contracts( + preconditions=[], + snapshots=[], + postconditions=[]), + non_mutating=False, + node=..., + arguments_by_name=..., + body=...)], + invariants=[], + serialization=Serialization( + with_model_type=True), + description=None, + node=..., + properties_by_name=..., + methods_by_name=...), + ConcreteClass( + name='Plain_marker', + inheritances=[ + 'Marker'], + properties=[], + methods=[ + ConstructorToBeUnderstood( + name='__init__', + verification=False, + visibility='PUBLIC', + arguments=[ + Argument( + name='self', + type_annotation=SelfTypeAnnotation(), + default=None, + node=...)], + returns=None, + description=None, + contracts=Contracts( + preconditions=[], + snapshots=[], + postconditions=[]), + non_mutating=False, + node=..., + arguments_by_name=..., + body=...)], + invariants=[], + serialization=None, + description=None, + node=..., + properties_by_name=..., + methods_by_name=...), ConcreteClass( name='Something', inheritances=[],