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=[],