diff --git a/Modules/Core/include/mitkGenericIDRelationRule.h b/Modules/Core/include/mitkGenericIDRelationRule.h index 36169d8dd3..f6e2c8fd96 100644 --- a/Modules/Core/include/mitkGenericIDRelationRule.h +++ b/Modules/Core/include/mitkGenericIDRelationRule.h @@ -1,132 +1,98 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkGenericIDRelationRule_h #define mitkGenericIDRelationRule_h #include "mitkPropertyRelationRuleBase.h" namespace mitk { /**This rule class can be used for relations that are only defined on the ID-layer and where no connection on the Data-layer can be defined or deduced. So it can be used for all ID based relations between PropertyProviders that also implement the interface identifiable. In order to be able to use this class for different relation types based on ID, the ruleIDTag is used. It must be specified when creating a rule instance. The ruleIDTag will be used as suffix for the rule ID of the instance and therefore allows to create specific and distinguishable rules instances based on this class. One may also specify the display name and the role names of the instance. If not speficied the default values are used (display name: " relation", source role name: "source of relation", destination role name: "destination of relation") */ class MITKCORE_EXPORT GenericIDRelationRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(GenericIDRelationRule, PropertyRelationRuleBase); itkCloneMacro(Self); mitkNewMacro1Param(Self, const RuleIDType &); mitkNewMacro2Param(Self, const RuleIDType &, const std::string &); mitkNewMacro4Param(Self, const RuleIDType &, const std::string &, const std::string &, const std::string &); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; /** Returns an ID string that identifies the rule class */ RuleIDType GetRuleID() const override; bool IsAbstract() const override; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ std::string GetDisplayName() const override; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ std::string GetSourceRoleName() const override; /** Returns a human readable string that can be used to describe the role of a destination in context of the rule * instance.*/ std::string GetDestinationRoleName() const override; /** Pass through to base implementation of PropertyRelationRuleBase. See PropertyRelationRuleBase::connect documentation for more information. */ RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const; protected: GenericIDRelationRule(const RuleIDType &ruleIDTag); GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName); GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole); ~GenericIDRelationRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; - /** Is called if a instance ID cannot be deduced on the ID-layer. - Implement this method to check which existing relation(s) as Connected_Data exists between - both passed instances. If the passed instances have no - explicit relation of type Connected_Data, an empty vector will be returned. - @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider - pair and rule. But the data layer may be ambiguous and there for muliple relation instances of the rule instance - could match. The implementation of this function should report all relation instances. The calling function - will take care of this violation. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance.*/ - InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, - const IPropertyProvider *destination) const override; - - /** Is called by HasRelation() if no relation of type Connected_ID (GetInstanceID_IDLayer()) or - Connected_Data (GetInstanceID_datalayer()) is evident. - Implement this method to deduce if the passed instances have a relation of type - Implicit_Data. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance. - */ - bool HasImplicitDataRelation(const IPropertyProvider *source, - const IPropertyProvider *destination) const override; - - /**Is called by Connect() to ensure that source has correctly set properties to resemble - the relation on the data layer. This means that the method should set the properties that describe - and encode the relation on the data layer (data-layer-specific relation properties). - If the passed instance are already connected, the old settings should be - overwritten. Connect() will ensure that source and destination are valid pointers. - @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance - is ensured. - @pre source must be a valid instance. - @pre destination must be a valid instance.*/ + using DataRelationUIDVectorType = PropertyRelationRuleBase::DataRelationUIDVectorType; + DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override; + void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override; - /**This method is called by Disconnect() to remove all properties of the relation from the source that - are set by Connect_datalayer(). - @remark All RII-properties of this relation will removed by Disconnect() after this method call. - If the relationUID is not part of the source. Nothing will be changed. Disconnect() ensures that source is a valid - pointer if called. - @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ - void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const override; + void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType& relationUID) const override; bool IsSupportedRuleID(const RuleIDType& ruleID) const override; itk::LightObject::Pointer InternalClone() const override; private: RuleIDType m_RuleIDTag; std::string m_DisplayName; std::string m_SourceRole; std::string m_DestinationRole; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkPropertyRelationRuleBase.h b/Modules/Core/include/mitkPropertyRelationRuleBase.h index 05e9523af5..dbaef1fa3b 100644 --- a/Modules/Core/include/mitkPropertyRelationRuleBase.h +++ b/Modules/Core/include/mitkPropertyRelationRuleBase.h @@ -1,337 +1,371 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkPropertyRelationRuleBase_h #define mitkPropertyRelationRuleBase_h #include "mitkIPropertyOwner.h" #include "mitkIdentifiable.h" #include "mitkException.h" #include "mitkNodePredicateBase.h" #include "mitkPropertyKeyPath.h" #include #include namespace mitk { /**Base class to standardize/abstract/encapsulate rules and business logic to detect and define (property/data based) relations in MITK. Following important definitions must be regarded when using/implementing/specifing rule classes: - Releations represented by rules are directed relations that point from a source IPropertyOwner (Source) to a destination IPropertyOwner (Destination). - Rule can be abstract (indicated by IsAbstract()) or concrete. Abstract rules cannot be used to connect relations. Abstract rules can only be used to detect/indicate or disconnect relations. Therefore, in contrast to concrete rules, abstract rules can be used to indicate several relations that are established be "derived" rules. See e.g. GenericIDRelationRule: in its abstract state it cannot connect but be used to detect any type of generic ID relation. - A concrete rule ID (rule ID of a concrete rule) always "implements" a concrete relation type. E.g. In DICOM the way to express the source image relation to an input image and to a mask would be nearly the same and only differs by the encoded purpose. One may implement an interim or joined class that manages the mutual stuff, but the registered instances must be one concrete rule for "DICOM source input image" and one concrete rule for "DICOM source mask" and both rules must have distinct rule IDs. - Source may have several relations of a rule to different Destinations. Destination may have several relations of a rule from different Sources. But a specific source destination pair may have only one relation of a specific rule id (concrete rule). A specific source destination pair may however have multiple relations for an abstract rule. - The deletion of a Destination in the storage does not remove the relation implicitly. It becomes a "zombie" relation but it should still be documented, even if the destination is unknown. One has to explicitly disconnect a zombie relation to get rid of it. - Each relation has its own UID (relationUID) that can be used to address it. The basic concept of the rule design is that we have two layers of relation identification: Layer 1 is the ID-layer which uses the IIdentifiable interface and UIDs if available to encode "hard" relations. Layer 2 is the Data-layer which uses the properties of Source and Destination to deduce if there is a relation of the rule type. - The ID-layer is completely implemented by this base class. The base class only falls back to the Data-layer - (implemented by the concrete rule class) if the ID-layer is not sufficient or it is explicitly stated. + The ID-layer is completely implemented by this base class. The base class falls back to the Data-layer + (implemented by the concrete rule class) if the ID-layer is not sufficient or it is explicitly stated to (only) + look at the data layer. Reasons for the introduction of the ID-layer are: 1st, data-defined relations may be weak (several Destinations are possible; e.g. DICOM source images may point to several loaded mitk images). But if explicitly a relation was connected it should be deduceable. 2nd, checks on a UID are faster then unnecessary data deduction. Rules use relation instance identifing (RII) properties in order to manage their relations that are stored in the Source. The RII-properties follow the following naming schema: - "MITK.Relations...[relationUID|destinationUID|ruleID|]" + "MITK.Relations..[relationUID|destinationUID|ruleID|]" - : The unique index of the relation for the Source. Used to assign/group the properties to their relation. In the default implementation of this class the instance id is an positive integer (i>0). - relationUID: The UID of the relation. Set by the ID-layer (so by this class) - destinationUID: The UID of the Destination. Set by the ID-layer (so by this class) if Destination implements IIdentifiable. - ruleID: The identifier of the concrete rule that sets the property. Is specified by the derived class and set automaticaly be this base class. - : Information needed by the Data-layer (so derived classes) to find the relationUID */ class MITKCORE_EXPORT PropertyRelationRuleBase : public itk::Object { public: mitkClassMacroItkParent(PropertyRelationRuleBase, itk::Object); itkCloneMacro(Self); itkCreateAnotherMacro(Self); using RuleIDType = std::string; using RelationUIDType = Identifiable::UIDType; using RelationUIDVectorType = std::vector; /** Enum class for different types of relations. */ enum class RelationType { None = 0, /**< Two IPropertyOwner have no relation under the rule.*/ - Implicit_Data = 1, /**< Two IPropertyOwner have a relation, but it is only deduced from the Data-layer and they - were never explicitly connected.*/ - Connected_Data = 2, /**< Two IPropertyOwner have a relation and there where connected on the Data-layer (so a bit + Data = 1, /**< Two IPropertyOwner have a relation, but it is "only" deduced from the Data-layer (so a bit "weaker" as ID). Reasons for the missing ID connection could be that Destintination has not IIdentifiable implemented.*/ - Connected_ID = 4 /**< Two IPropertyOwner have a relation and are fully explictly connected.*/ + ID = 2, /**< Two IPropertyOwner have a relation and are explictly connected via the ID of IIdentifiable of the Destination.*/ + Complete = 3 /**< Two IPropertyOwner have a relation and are fully explictly connected (via data layer and ID layer).*/ }; + using RelationVectorType = std::vector; /** Returns the generic root path for relation rules ("MITK.Relations").*/ static PropertyKeyPath GetRootKeyPath(); /** Returns an ID string that identifies the rule class. @post The returned rule ID must met the preconditions of a PropertyKeyPath element name (see mitk::PropertyKeyPath*/ virtual RuleIDType GetRuleID() const = 0; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ virtual std::string GetDisplayName() const = 0; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ virtual std::string GetSourceRoleName() const = 0; /** Returns a human readable string that can be used to describe the role of a destionation in context of the rule * instance.*/ virtual std::string GetDestinationRoleName() const = 0; /** Returns if the instance is a abstract rule (true). Default implementation is true. Overwrite and reimplement if another behavior is needed.*/ virtual bool IsAbstract() const; /** This method checks if owner is eligible to be a Source for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsSourceCandidate(const IPropertyProvider *owner) const; /** This method checks if owner is eligible to be a Destination for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsDestinationCandidate(const IPropertyProvider *owner) const; - /** Returns true if the passed owner is a Source of a relation defined by the rule so has at least one relation of - type Connected_Data or Connected_ID. + /** Returns true if the passed owner is a Source of a relation defined by the rule. @pre owner must be a pointer to a valid IPropertyProvider instance.*/ bool IsSource(const IPropertyProvider *owner) const; - /** Returns the relation type of the passed IPropertyOwner instances. + /** Returns all relation types of the passed IPropertyOwner instances. + @remark Abstract rules may have several relationtypes between the instances (from different supported concrete rules), that cover + both ID and Data relations; thus it returns a vector of RelationTypes. + @result Vector of all relation types that exist between the given instances. Empty vector equals none relation at all. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance. */ - RelationType HasRelation(const IPropertyProvider *source, const IPropertyProvider *destination) const; + RelationVectorType GetRelationTypes(const IPropertyProvider* source, const IPropertyProvider* destination) const; + + /** Indicates if passed IPropertyOwner instances have a relation of a certain type. + @remark Abstract rules may also indicate RelationType::Complete if there + are multiple relations (from different supported concrete rules), that cover + both ID and Data relations. + @param requiredRelation Defines the type of relation that should be present. None: does not matter which one, as long as at least one is present. + Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers. + @pre source must be a pointer to a valid IPropertyProvider instance. + @pre destination must be a pointer to a valid IPropertyProvider instance. + */ + bool HasRelation(const IPropertyProvider *source, const IPropertyProvider *destination, RelationType requiredRelation = RelationType::None) const; /** Returns a vector of relation UIDs for all relations of this rule instance that are defined for - the passed source (so relations of type Connected_Data and Connected_ID). + the passed source. @pre source must be a pointer to a valid IPropertyOwner instance. - */ - RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source) const; + @param layer Defines the layer the relations must be reflected. None: does not matter which one, as long as at least one is present. + Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers.*/ + RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source, RelationType layer = RelationType::None) const; /** Returns the relation UID(s) for the passed source and destination of this rule instance. If the rule is abstract multiple relation UIDs might be returned. In case of concrete rule only one relation UID. - If the passed instances have no explicit relation (so of type Connected_Data or Connected_ID), - no ID can be deduced and an empty relation UID vector is returned. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance.*/ RelationUIDVectorType GetRelationUIDs(const IPropertyProvider *source, const IPropertyProvider *destination) const; /** Returns the relation UID for the passed source and destination of this rule instance. - If the passed instances have no explicit relation (so of type Connected_Data or Connected_ID), - no ID can be deduced and an exception will be thrown. If more then one relation is found, also - an exception will be thrown. Thus only use this convinience method, if you are sure that one(!) - relation UID can exist. + If the passed instances have no relation, no ID can be deduced and an exception will be thrown. + If more then one relation is found, also an exception will be thrown. Thus only use this convinience method, + if you are sure that one(!) relation UID can exist. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance. - @pre Source and destination have one relation of type Connected_Data or Connected_ID; otherwise + @pre Source and destination have one relation; otherwise if no relation exists a NoPropertyRelationException is thrown; if more than one relation exists a default MITK expception is thrown.*/ RelationUIDType GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const; /**Predicate that can be used to find nodes that qualify as source for that rule (but must not be a source yet). Thus all nodes where IsSourceCandidate() returns true. */ NodePredicateBase::ConstPointer GetSourceCandidateIndicator() const; /**Predicate that can be used to find nodes that qualify as destination for that rule (but must not be a destination yet). Thus all nodes where IsDestinationCandidate() returns true. */ NodePredicateBase::ConstPointer GetDestinationCandidateIndicator() const; - /**Predicate that can be used to find nodes that are Sources of that rule and explicitly connected. + /**Predicate that can be used to find nodes that are Sources of that rule and connected. Thus all nodes where IsSource() returns true.*/ NodePredicateBase::ConstPointer GetConnectedSourcesDetector() const; /**Predicate that can be used to find nodes that are as source related to the passed Destination under the rule @param destination Pointer to the Destination instance that should be used for detection. - @param minimalRelation Defines the minimal strength of the relation type that should be detected. + @param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default); + Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations. @pre Destination must be a valid instance.*/ NodePredicateBase::ConstPointer GetSourcesDetector( - const IPropertyProvider *destination, RelationType minimalRelation = RelationType::Implicit_Data) const; + const IPropertyProvider *destination, RelationType exclusiveRelation = RelationType::None) const; /**Predicate that can be used to find nodes that are as Destination related to the passed Source under the rule @param source Pointer to the Source instance that should be used for detection. - @param minimalRelation Defines the minimal strength of the relation type that should be detected. + @param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default); + Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations. @pre Destination must be a valid instance.*/ NodePredicateBase::ConstPointer GetDestinationsDetector( - const IPropertyProvider *source, RelationType minimalRelation = RelationType::Implicit_Data) const; + const IPropertyProvider *source, RelationType exclusiveRelation = RelationType::None) const; /**Returns a predicate that can be used to find the Destination of the passed Source for a given relationUID. @param source Pointer to the Source instance that should be used for detection. - @param minimalRelation Defines the minimal strength of the relation type that should be detected. @pre source must be a valid instance. @pre relationUID must identify a relation of the passed source and rule. (This must be in the return of this->GetExistingRelations(source). */ NodePredicateBase::ConstPointer GetDestinationDetector(const IPropertyProvider *source, RelationUIDType relationUID) const; - /**Disconnect the passed instances. Afterwards they have a relation of None or Implicit_Data. - All RII-properties in the source for the passed destination will be removed. + /**Disconnect the passed instances by modifing source. One can specify which layer should be disconnected + via the argument "layer". Default is the complete disconnection. + All RII-properties or properties that define the connection on the data layer in the source + for the passed destination will be removed. @pre source must be a valid instance. - @pre destination must be a valid instance.*/ - void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination) const; + @pre destination must be a valid instance. + @param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection + on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/ + void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer = RelationType::Complete) const; /**Disconnect the source from the passed relationUID (usefull for "zombie relations"). - All RII-properties in the source for the passed relationUID will be removed. + One can specify which layer should be disconnected + via the argument "layer". Default is the complete disconnection. + All RII-properties or properties that define the connection on the data layer in the source + for the passed destination will be removed. If the relationUID is not part of the source. Nothing will be changed. - @pre source must be a valid instance.*/ - void Disconnect(IPropertyOwner *source, RelationUIDType relationUID) const; + @pre source must be a valid instance. + @param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection + on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/ + void Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer = RelationType::Complete) const; + + /**Returns the list of PropertyKeyPaths of all properties that are relevant for a given relation. + @param source Pointer to the Source instance that containes the potential properties. + @param relationUID UID of the relation that is relevant for the requested properties. + @param layer Indicates which layer is requested. ID: returns all RII properties that belong to the relation. Data: returns all properties that are relevant/belong to the data layer of the relation. Complete: returns all properties (ID+Data) + @pre source must be a valid instance. + @pre relationUID must identify a relation of the passed source and rule. (This must be in the return of + this->GetExistingRelations(source). */ + std::vector GetReleationPropertyPaths(const IPropertyProvider* source, + RelationUIDType relationUID, RelationType layer = RelationType::Data) const; protected: PropertyRelationRuleBase() = default; ~PropertyRelationRuleBase() override = default; using InstanceIDType = std::string; using InstanceIDVectorType = std::vector; static InstanceIDType NULL_INSTANCE_ID(); /** Returns the instance IDs for the passed source and destination for this rule instance. If the passed source and destination instances has no explicit relation on the ID layer (Connected_ID), an empty vector will be returned. @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider - pair and concrete rule. But for there might be more then one instanceID, because the rule is abstract and - supports multiple rule IDs. 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance + pair and concrete rule. But there might be more then one instanceID because either 1) the rule is abstract and + supports multiple rule IDs or 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance could match. The implementation of this function should report all relation instances. The calling function will take care. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance.*/ InstanceIDVectorType GetInstanceID_IDLayer(const IPropertyProvider *source, const IPropertyProvider *destination) const; - /** Is called if an instance ID cannot be deduced on the ID-layer. - Implement this method to check which existing relation(s) as Connected_Data exists between - both passed instances. If the passed instances have no - explicit relation of type Connected_Data, an empty vector will be returned. - @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider - pair and concrete rule. But for 2 reasons there might be more then one instanceID: 1) the rule is abstract and - supports multiple rule IDs. 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance - could match. The implementation of this function should report all relation instances. The calling function + using DataRelationUIDVectorType = std::vector< std::pair >; + /** Returns the ReleationUIDs of all relations that are defined by the data layer of source for + this rule instance and, if defined, destination. + If the passed source (and destination) instance has no relation on the data layer, + an empty vector will be returned. + @remark Per definition for property relation rules only 0 or 1 instance should be found for one provider + pair and concrete rule. But there might be more then one instance because either 1) the rule is abstract and + supports multiple rule IDs or 2) the data layer may be ambiguous (e.g. because the destination was not specified) + and therefore multiple relation instances of the rule instance could match. + The implementation of this function should report all relation instances. The calling function will take care. @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance.*/ - virtual InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, - const IPropertyProvider *destination) const = 0; - - /** Is called by HasRelation() if no relation of type Connected_ID (GetInstanceID_IDLayer()) or - Connected_Data (GetInstanceID_datalayer()) is evident. - Implement this method to deduce if the passed instances have a relation of type - Implicit_Data. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance. - */ - virtual bool HasImplicitDataRelation(const IPropertyProvider *source, - const IPropertyProvider *destination) const = 0; + @param destination Destination the find relations should point to. If destination is NULL any relation + on the data layer for this rule and source are wanted. + @param instances_IDLayer List of releation instances that are already defined by the ID layer. The implementation of this + function should only cover releations that are not already resembled in the passed relarions_IDLayer.*/ + virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider * source, + const IPropertyProvider * destination, const InstanceIDVectorType& instances_IDLayer) const = 0; /**Helper function that deduces the relation UID of the given relation instance. If it cannot be deduced an NoPropertyRelationException is thrown.*/ RelationUIDType GetRelationUIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; /**Helper function that deduces the relation instance ID given the relation UID. If it cannot be deduced an NoPropertyRelationException is thrown.*/ InstanceIDType GetInstanceIDByRelationUID(const IPropertyProvider *source, const RelationUIDType &relationUID) const; - /**Explicitly connects the passed instances. Afterwards they have a relation of Connected_Data or Connected_ID (if a - destination implements IIdentifiable). If the passed instance are already connected the old connection will be - overwritten (and raised to the highest possible connection level). + /**Explicitly connects the passed instances. Afterwards they have a relation of Data (if data layer is supported), ID (if a + destination implements IIdentifiable) or Complete (if Data and ID could be connected). If the passed instance are already + connected the old connection will be overwritten (and raised to the highest possible connection level). @remark This method has protected visibility in the base implementation, because it is a design decision of derived rule classes which interface they want to offer for connecting. It may just be made public (e.g. GenericIDRelationRule) or used by own implementations. @pre source must be a valid instance. @pre destination must be a valid instance. @pre the rule instance must not be abstract. @return Return the relation uid of the relation connected by this method call*/ RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const; /**Is called by Connect() to ensure that source has correctly set properties to resemble the relation on the data layer. This means that the method should set the properties that describe and encode the relation on the data layer (data-layer-specific relation properties). If the passed instance are already connected, the old settings should be overwritten. Connect() will ensure that source and destination are valid pointers. @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance is ensured. @pre source must be a valid instance. @pre destination must be a valid instance.*/ virtual void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const = 0; /**This method is called by Disconnect() to remove all properties of the relation from the source that are set by Connect_datalayer(). - @remark All RII-properties of this relation will removed by Disconnect() after this method call. - If the relationUID is not part of the source. Nothing will be changed. Disconnect() ensures that source is a valid + @remark This method should remove all properties that are set for a specific relation by Connect_datalayer(...). + If the relationUID is not part of the source, nothing will be changed. Disconnect() ensures that source is a valid pointer if called. @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ - virtual void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const = 0; + virtual void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const = 0; /** Returns if the passed rule ID is supported/relevant for the rule. Either because it is the very ID of the rule (default implementation) or because it is an abstract rule which also supports the rule ID. @return true: If the rule ID can handle the rule ID. false: the rule does not support the rule ID.*/ virtual bool IsSupportedRuleID(const RuleIDType& ruleID) const; /** Helper function that generates a reg ex that can be used to find a specific RII property for the rule instance. * @param propName If not empty a PropertyPath element will added (with the passed value) after the element. * @param instanceID If not empty only for the reg ex will only valid for the passed instanceID. Otherwise for all.*/ std::string GetRIIPropertyRegEx(const std::string propName = "", const InstanceIDType &instanceID = "") const; /**Helper function that deduces the instance ID out of a property name. If it cannot be deduced an MITK exception is thrown.*/ static InstanceIDType GetInstanceIDByPropertyName(const std::string propName); /**Helper function that retrives the rule ID of a relation instance of a passed source. @pre source must be valid. @pre source must have a relation instance with this ID*/ RuleIDType GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; + /**Helper function that retrives the destinatuon UID of a relation instance of a passed + source. If the relation has no destination UID, an empty string will be returned. + @pre source must be valid.*/ + std::string GetDestinationUIDByInstanceID(const IPropertyProvider * source, + const InstanceIDType & instanceID) const; itk::LightObject::Pointer InternalClone() const override; - static std::vector GetPropertyKeys(const mitk::IPropertyProvider *owner); + /** helper method that serves as a workaround until T24729 is done. + Please remove if T24728 is done then could directly use owner->GetPropertyKeys() again.*/ + static std::vector GetPropertyKeys(const IPropertyProvider *owner); private: /** Creats a relation UID*/ static RelationUIDType CreateRelationUID(); /**Prepares a new relation instance. Therefore an unused and valid instance ID for the passed source will be genarated and a relationUID property with the relationUID will be set to block the instance ID. The instance ID will be returned. @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently.*/ InstanceIDType CreateNewRelationInstance(IPropertyOwner *source, const RelationUIDType &relationUID) const; }; /**Exception that is used by PropertyRelationRuleBase based classes to indicate that two objects have no relation.*/ class NoPropertyRelationException : public Exception { public: mitkExceptionClassMacro(NoPropertyRelationException, Exception) }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkSourceImageRelationRule.h b/Modules/Core/include/mitkSourceImageRelationRule.h index 3b33d42e80..037959ca07 100644 --- a/Modules/Core/include/mitkSourceImageRelationRule.h +++ b/Modules/Core/include/mitkSourceImageRelationRule.h @@ -1,160 +1,129 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkGenericIDRelationRule_h #define mitkGenericIDRelationRule_h #include "mitkPropertyRelationRuleBase.h" #include "mitkImage.h" namespace mitk { /**This rule class can be used for relations that reference an image as source for a destination entity. (e.g. an image that is used to generate the relation source). The ID-layer is supported like for GenericIDReleations. So it can be used for all ID based relations between PropertyProviders that also implement the interface identifiable. In addition the rule uses the data-layer to deduce/define relations. For this layer it uses properties compliant to DICOM. Thus (1) the information is stored in a DICOM Source Image Sequence item (0x0008,0x2112) and (2) the destination must have properties DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016). If the destination does not have this properties, no connection can be made on the data-layer. @remark Please note that PropertyRelationRules and DICOM use the term "source" differently. The DICOM source (image) equals the PropertyRelationRule destination. This is due to an inverted relation direction. So in the context of the SourceImageRelationRule interface a derived data is the source and points to the original image, it derives from. In the context of DICOM this referenced original image would be called source image (as the name of this class). In order to be able to use this class for different relation types (DICOM would call it purposes), the purposeTag is used. It must be specified when creating a rule instance. The purposeTag will be used as suffix for the rule ID of the instance and therefore allows to create specific and distinguishable rules instances based on this class. One may also specify the display name and the role names of the instance. If not specified the default values are used (display name: " relation", source role name: "derived data", destination role name: "source image") */ class MITKCORE_EXPORT SourceImageRelationRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(SourceImageRelationRule, PropertyRelationRuleBase); itkNewMacro(Self); mitkNewMacro1Param(Self, const RuleIDType &); mitkNewMacro2Param(Self, const RuleIDType &, const std::string &); mitkNewMacro4Param(Self, const RuleIDType &, const std::string &, const std::string &, const std::string &); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; /** Returns an ID string that identifies the rule class */ RuleIDType GetRuleID() const override; bool IsAbstract() const override; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ std::string GetDisplayName() const override; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ std::string GetSourceRoleName() const override; /** Returns a human readable string that can be used to describe the role of a destination in context of the rule * instance.*/ std::string GetDestinationRoleName() const override; bool IsDestinationCandidate(const IPropertyProvider *owner) const override; /** Connects to passed images. @remark destination must specifiy DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016) in order to establish a connection on the data layer.*/ RelationUIDType Connect(Image *source, const Image *destination) const; protected: SourceImageRelationRule(); SourceImageRelationRule(const RuleIDType &purposeTag); SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName); SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole); ~SourceImageRelationRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; - /** Helper function that returns a vector of all selections of the property DICOM.0008.2112 that refer to destination or all (if no destination is passed).*/ - std::vector GetReferenceSequenceIndices(const IPropertyProvider * source, - const IPropertyProvider * destination = nullptr) const; - - /** Is called if a instance ID cannot be deduced on the ID-layer. - Implement this method to check which existing relation(s) as Connected_Data exists between - both passed instances. If the passed instances have no - explicit relation of type Connected_Data, an empty vector will be returned. - @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider - pair and rule. But the data layer may be ambiguous and there for muliple relation instances of the rule instance - could match. The implementation of this function should report all relation instances. The calling function - will take care of this violation. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance.*/ - InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, - const IPropertyProvider *destination) const override; - - /** Is called by HasRelation() if no relation of type Connected_ID (GetInstanceID_IDLayer()) or - Connected_Data (GetInstanceID_datalayer()) is evident. - Implement this method to deduce if the passed instances have a relation of type - Implicit_Data. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance. - */ - bool HasImplicitDataRelation(const IPropertyProvider *source, - const IPropertyProvider *destination) const override; - - /**Is called by Connect() to ensure that source has correctly set properties to resemble - the relation on the data layer. This means that the method should set the properties that describe - and encode the relation on the data layer (data-layer-specific relation properties). - If the passed instance are already connected, the old settings should be - overwritten. Connect() will ensure that source and destination are valid pointers. - @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance - is ensured. - @pre source must be a valid instance. - @pre destination must be a valid instance.*/ + /** Helper function that returns a vector of all selections of the property DICOM.0008.2112 (and its associated ruleID) that refer to destination or all (if no destination is passed) with a supported RuleID + and which are not already covered by the ignoreInstances.*/ + std::vector > GetReferenceSequenceIndices(const IPropertyProvider * source, + const IPropertyProvider* destination = nullptr, InstanceIDVectorType ignoreInstances = {}) const; + + using DataRelationUIDVectorType = PropertyRelationRuleBase::DataRelationUIDVectorType; + virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override; + void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override; - /**This method is called by Disconnect() to remove all properties of the relation from the source that - are set by Connect_datalayer(). - @remark All RII-properties of this relation will removed by Disconnect() after this method call. - If the relationUID is not part of the source. Nothing will be changed. Disconnect() ensures that source is a valid - pointer if called. - @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ - void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const override; + void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType& relationUID) const override; bool IsSupportedRuleID(const RuleIDType& ruleID) const override; itk::LightObject::Pointer InternalClone() const override; /**Prepares a new reference to an image on the data layer. Therefore an unused and valid sequence item index for the passed source will be genarated and a relationUID property with the relationUID will be set to block the instance ID. The instance ID will be returned. @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently.*/ PropertyKeyPath::ItemSelectionIndex CreateNewSourceImageSequenceItem(IPropertyOwner *source) const; + std::string GenerateRuleID(const std::string& purpose) const; + private: RuleIDType m_PurposeTag; std::string m_DisplayName; std::string m_SourceRole; std::string m_DestinationRole; }; } // namespace mitk #endif diff --git a/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp b/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp index 7681bb1fb2..5d5bae9ea7 100644 --- a/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp +++ b/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp @@ -1,92 +1,85 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkGenericIDRelationRule.h" bool mitk::GenericIDRelationRule::IsAbstract() const { return m_RuleIDTag.empty(); }; bool mitk::GenericIDRelationRule::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID() || (IsAbstract() && ruleID.find("IDRelation_") == 0); }; mitk::GenericIDRelationRule::RuleIDType mitk::GenericIDRelationRule::GetRuleID() const { return "IDRelation_" + m_RuleIDTag; }; std::string mitk::GenericIDRelationRule::GetDisplayName() const { return m_DisplayName; }; std::string mitk::GenericIDRelationRule::GetSourceRoleName() const { return m_SourceRole; }; std::string mitk::GenericIDRelationRule::GetDestinationRoleName() const { return m_DestinationRole; }; mitk::GenericIDRelationRule::RelationUIDType mitk::GenericIDRelationRule::Connect(IPropertyOwner *source, const IPropertyProvider *destination) const { return Superclass::Connect(source, destination); }; mitk::GenericIDRelationRule::GenericIDRelationRule(const RuleIDType &ruleIDTag) : GenericIDRelationRule(ruleIDTag, ruleIDTag + " relation"){}; mitk::GenericIDRelationRule::GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName) : GenericIDRelationRule( ruleIDTag, displayName, "source of " + ruleIDTag + " relation", "destination of " + ruleIDTag + " relation"){}; mitk::GenericIDRelationRule::GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole) : m_RuleIDTag(ruleIDTag), m_DisplayName(displayName), m_SourceRole(sourceRole), m_DestinationRole(destinationRole){}; -mitk::GenericIDRelationRule::InstanceIDVectorType mitk::GenericIDRelationRule::GetInstanceID_datalayer( - const IPropertyProvider * /*source*/, const IPropertyProvider * /*destination*/) const +mitk::GenericIDRelationRule::DataRelationUIDVectorType mitk::GenericIDRelationRule::GetRelationUIDs_DataLayer( + const IPropertyProvider * /*source*/, const IPropertyProvider * /*destination*/, const InstanceIDVectorType& /*instances_IDLayer*/) const { // Data layer is not supported by the class. Therefore return empty vector. - return InstanceIDVectorType(); -}; - -bool mitk::GenericIDRelationRule::HasImplicitDataRelation(const IPropertyProvider * /*source*/, - const IPropertyProvider * /*destination*/) const -{ - // Data layer is not supported by the class. - return false; + return DataRelationUIDVectorType(); }; void mitk::GenericIDRelationRule::Connect_datalayer(IPropertyOwner * /*source*/, const IPropertyProvider * /*destination*/, const InstanceIDType & /*instanceID*/) const { // Data layer is not supported by the class. => Do nothing }; -void mitk::GenericIDRelationRule::Disconnect_datalayer(IPropertyOwner * /*source*/, const InstanceIDType & /*instanceID*/) const { +void mitk::GenericIDRelationRule::Disconnect_datalayer(IPropertyOwner * /*source*/, const RelationUIDType & /*relationUID*/) const { // Data layer is not supported by the class. => Do nothing }; itk::LightObject::Pointer mitk::GenericIDRelationRule::InternalClone() const { itk::LightObject::Pointer result = Self::New(this->m_RuleIDTag, this->m_DisplayName, this->m_SourceRole, this->m_DestinationRole).GetPointer(); return result; }; diff --git a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp index 8bfd7d09d6..407b288720 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp @@ -1,758 +1,855 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPropertyRelationRuleBase.h" #include #include #include #include #include #include #include +#include bool mitk::PropertyRelationRuleBase::IsAbstract() const { return true; }; bool mitk::PropertyRelationRuleBase::IsSourceCandidate(const IPropertyProvider *owner) const { return owner != nullptr; }; bool mitk::PropertyRelationRuleBase::IsDestinationCandidate(const IPropertyProvider *owner) const { return owner != nullptr; }; mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRootKeyPath() { return PropertyKeyPath().AddElement("MITK").AddElement("Relations"); }; bool mitk::PropertyRelationRuleBase::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID(); }; std::string mitk::PropertyRelationRuleBase::GetRIIPropertyRegEx(const std::string propName, const InstanceIDType &instanceID) const { auto path = this->GetRootKeyPath(); if (instanceID.empty()) { path.AddAnyElement(); } else { path.AddElement(instanceID); } if (!propName.empty()) { path.AddElement(propName); } return PropertyKeyPathToPropertyRegEx(path); }; //workaround until T24729 is done. Please remove if T24728 is done //then could directly use owner->GetPropertyKeys() again. std::vector mitk::PropertyRelationRuleBase::GetPropertyKeys(const mitk::IPropertyProvider *owner) { std::vector keys; auto sourceCasted = dynamic_cast(owner); if (sourceCasted) { auto sourceData = sourceCasted->GetData(); if (sourceData) { keys = sourceData->GetPropertyKeys(); } else { keys = sourceCasted->GetPropertyKeys(); } } else { keys = owner->GetPropertyKeys(); } return keys; }; //end workaround for T24729 - bool mitk::PropertyRelationRuleBase::IsSource(const IPropertyProvider *owner) const { - if (!owner) - { - mitkThrow() << "Error. Passed owner pointer is NULL"; - } + return !this->GetExistingRelations(owner).empty(); +}; - std::vector keys; - //workaround until T24729 is done. Please remove if T24728 is done - keys = GetPropertyKeys(owner); - //end workaround for T24729 +bool mitk::PropertyRelationRuleBase::HasRelation( + const IPropertyProvider* source, const IPropertyProvider* destination, RelationType requiredRelation) const +{ + auto relTypes = this->GetRelationTypes(source, destination); - auto sourceRegExStr = this->GetRIIPropertyRegEx("ruleID"); - auto regEx = std::regex(sourceRegExStr); - for (const auto &key : keys) + if (requiredRelation == RelationType::None) { - if (std::regex_match(key, regEx)) - { - auto idProp = owner->GetConstProperty(key); - auto ruleID = idProp->GetValueAsString(); - if (this->IsSupportedRuleID(ruleID)) - { - return true; - } - } + return !relTypes.empty(); } - return false; -}; + RelationVectorType allowedTypes = { RelationType::Complete }; + if (requiredRelation == RelationType::Data) + { + allowedTypes.emplace_back(RelationType::Data); + } + else if (requiredRelation == RelationType::ID) + { + allowedTypes.emplace_back(RelationType::ID); + } -mitk::PropertyRelationRuleBase::RelationType mitk::PropertyRelationRuleBase::HasRelation( - const IPropertyProvider *source, const IPropertyProvider *destination) const + return relTypes.end() != std::find_first_of(relTypes.begin(), relTypes.end(), allowedTypes.begin(), allowedTypes.end()); +} + +mitk::PropertyRelationRuleBase::RelationVectorType mitk::PropertyRelationRuleBase::GetRelationTypes( + const IPropertyProvider* source, const IPropertyProvider* destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed owner pointer is NULL"; } - RelationType result = RelationType::None; - - if (!this->GetInstanceID_IDLayer(source, destination).empty()) - { // has relations of type Connected_ID; - result = RelationType::Connected_ID; + auto instanceIDs_IDLayer = this->GetInstanceID_IDLayer(source, destination); + auto relIDs_dataLayer = this->GetRelationUIDs_DataLayer(source, destination, {}); + if (relIDs_dataLayer.size() > 1) + { + MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Relation UID: " + << relIDs_dataLayer.front().first; } - if (result == RelationType::None) + bool hasComplete = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { - auto instanceIDs_data = this->GetInstanceID_datalayer(source, destination); + auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); + auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); - if (instanceIDs_data.size() > 1) + return relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { - MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Instance ID: " - << instanceIDs_data.front(); - } + return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; + }); + }); - if (!instanceIDs_data.empty() && instanceIDs_data.front() != NULL_INSTANCE_ID()) - { // has relations of type Connected_Data; - result = RelationType::Connected_Data; - } - } + bool hasID = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) + { + auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); + auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); + + return relIDs_dataLayer.end() == std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) + { + return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; + }); + }); - if (result == RelationType::None) + bool hasData = relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { - if (this->HasImplicitDataRelation(source, destination)) - { // has relations of type Connected_Data; - result = RelationType::Implicit_Data; - } + return instanceIDs_IDLayer.end() == std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) + { + auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); + auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); + return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; + }); + }); + + RelationVectorType result; + + if (hasData) + { + result.emplace_back(RelationType::Data); + } + if (hasID) + { + result.emplace_back(RelationType::ID); + } + if (hasComplete) + { + result.emplace_back(RelationType::Complete); } return result; }; mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetExistingRelations( - const IPropertyProvider *source) const + const IPropertyProvider *source, RelationType layer) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } - auto ruleIDRegExStr = this->GetRIIPropertyRegEx("ruleID"); - auto regEx = std::regex(ruleIDRegExStr); - - //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. - const auto keys = GetPropertyKeys(source); - //end workaround for T24729 - RelationUIDVectorType relationUIDs; + InstanceIDVectorType instanceIDs; - for (const auto &key : keys) + if (layer != RelationType::Data) { - if (std::regex_match(key, regEx)) + auto ruleIDRegExStr = this->GetRIIPropertyRegEx("ruleID"); + auto regEx = std::regex(ruleIDRegExStr); + + //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. + const auto keys = GetPropertyKeys(source); + //end workaround for T24729 + + for (const auto& key : keys) { - auto idProp = source->GetConstProperty(key); - auto ruleID = idProp->GetValueAsString(); - if (this->IsSupportedRuleID(ruleID)) + if (std::regex_match(key, regEx)) { - auto instanceID = this->GetInstanceIDByPropertyName(key); - relationUIDs.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); + auto idProp = source->GetConstProperty(key); + auto ruleID = idProp->GetValueAsString(); + if (this->IsSupportedRuleID(ruleID)) + { + auto instanceID = this->GetInstanceIDByPropertyName(key); + instanceIDs.emplace_back(instanceID); + relationUIDs.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); + } } } } - return relationUIDs; + if (layer == RelationType::ID) + { + return relationUIDs; + } + + DataRelationUIDVectorType relationUIDandRuleID_Data; + if (layer != RelationType::ID) + { + relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source, nullptr, instanceIDs); + } + + RelationUIDVectorType relationUIDs_Data; + std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relationUIDs_Data), + [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); + + if (layer == RelationType::Data) + { + return relationUIDs_Data; + } + + std::sort(relationUIDs.begin(), relationUIDs.end()); + std::sort(relationUIDs_Data.begin(), relationUIDs_Data.end()); + + RelationUIDVectorType result; + + if (layer == RelationType::Complete) + { + std::set_intersection(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); + } + else + { + std::set_union(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); + } + + return result; }; mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetRelationUIDs( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } - RelationUIDVectorType result; + RelationUIDVectorType relUIDs_id; auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); for (const auto instanceID : instanceIDs) { - result.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); + relUIDs_id.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } - if (result.empty() || this->IsAbstract()) - { - auto instanceIDs_data = this->GetInstanceID_datalayer(source, destination); + DataRelationUIDVectorType relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source,destination,instanceIDs); + RelationUIDVectorType relUIDs_Data; + std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relUIDs_Data), + [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); - for (const auto instanceID : instanceIDs_data) - { - if (std::find(std::begin(instanceIDs), std::end(instanceIDs), instanceID) == std::end(instanceIDs)) - { - result.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); - } - } - } + std::sort(relUIDs_id.begin(), relUIDs_id.end()); + std::sort(relUIDs_Data.begin(), relUIDs_Data.end()); + + RelationUIDVectorType result; + std::set_union(relUIDs_id.begin(), relUIDs_id.end(), relUIDs_Data.begin(), relUIDs_Data.end(), std::back_inserter(result)); return result; }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const { auto result = this->GetRelationUIDs(source, destination); if (result.empty()) { mitkThrowException(NoPropertyRelationException); } else if(result.size()>1) { mitkThrow() << "Cannot return one(!) relation UID. Multiple relations exists for given rule, source and destination."; } return result[0]; }; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::NULL_INSTANCE_ID() { return std::string(); }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUIDByInstanceID( const IPropertyProvider *source, const InstanceIDType &instanceID) const { RelationUIDType result; if (instanceID != NULL_INSTANCE_ID()) { auto idProp = source->GetConstProperty( PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(instanceID).AddElement("relationUID"))); if (idProp.IsNotNull()) { result = idProp->GetValueAsString(); } } if (result.empty()) { mitkThrowException(NoPropertyRelationException); } return result; }; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByRelationUID( const IPropertyProvider *source, const RelationUIDType &relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } InstanceIDType result = NULL_INSTANCE_ID(); auto destRegExStr = PropertyKeyPathToPropertyRegEx(GetRootKeyPath().AddAnyElement().AddElement("relationUID")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == relationUID) { if (instance_matches.size()>1) { result = instance_matches[1]; break; } } } } return result; }; mitk::PropertyRelationRuleBase::InstanceIDVectorType mitk::PropertyRelationRuleBase::GetInstanceID_IDLayer( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } auto identifiable = dynamic_cast(destination); if (!identifiable) { //This check and pass through to data is needed due to solve T25711. See Task for more information. //This could be removed at the point we can get rid of DataNodes or they get realy transparent. auto node = dynamic_cast(destination); if (node && node->GetData()) { identifiable = dynamic_cast(node->GetData()); } } InstanceIDVectorType result; if (identifiable) { // check for relations of type Connected_ID; auto destRegExStr = this->GetRIIPropertyRegEx("destinationUID"); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto destUID = identifiable->GetUID(); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == destUID) { if (instance_matches.size()>1) { auto instanceID = instance_matches[1]; if (this->IsSupportedRuleID(GetRuleIDByInstanceID(source, instanceID))) { result.push_back(instanceID); } } } } } } return result; }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::Connect(IPropertyOwner *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } if (this->IsAbstract()) { mitkThrow() << "Error. This is an abstract property relation rule. Abstract rule must not make a connection. Please use a concrete rule."; } auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); bool hasIDlayer = !instanceIDs.empty(); - auto instanceIDs_data = this->GetInstanceID_datalayer(source, destination); - if (instanceIDs_data.size() > 1) - { - MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Instance ID: " - << instanceIDs_data.front(); - } - bool hasDatalayer = !instanceIDs_data.empty(); + auto relUIDs_data = this->GetRelationUIDs_DataLayer(source, destination, {}); - if (hasIDlayer && hasDatalayer && instanceIDs.front() != instanceIDs_data.front()) + if (relUIDs_data.size() > 1) { - mitkThrow() << "Property relation information is in an invalid state. ID and data layer point to different " - "relation instances. Rule: " - << this->GetRuleID() << "; ID based instance: " << instanceIDs.front() - << "; Data base instance: " << instanceIDs_data.front(); + MITK_WARN << "Property relation on data level is ambiguous. First relation is used. RelationUID ID: " + << relUIDs_data.front().first; } + bool hasDatalayer = !relUIDs_data.empty(); + RelationUIDType relationUID = this->CreateRelationUID(); - InstanceIDType instanceID = ""; + InstanceIDType instanceID = NULL_INSTANCE_ID(); if (hasIDlayer) { instanceID = instanceIDs.front(); } else if (hasDatalayer) { - instanceID = instanceIDs_data.front(); + try + { + instanceID = this->GetInstanceIDByRelationUID(source, relUIDs_data.front().first); + } + catch(...) + { } } - else + + if(instanceID == NULL_INSTANCE_ID()) { instanceID = this->CreateNewRelationInstance(source, relationUID); } auto relUIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("relationUID")); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); auto ruleIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("ruleID")); source->SetProperty(ruleIDKey, mitk::StringProperty::New(this->GetRuleID())); if (!hasIDlayer) { auto identifiable = dynamic_cast(destination); if (!identifiable) { //This check and pass through to data is needed due to solve T25711. See Task for more information. //This could be removed at the point we can get rid of DataNodes or they get realy transparent. auto node = dynamic_cast(destination); if (node && node->GetData()) { identifiable = dynamic_cast(node->GetData()); } } if (identifiable) { auto destUIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("destinationUID")); source->SetProperty(destUIDKey, mitk::StringProperty::New(identifiable->GetUID())); } } - if (!hasDatalayer) - { - this->Connect_datalayer(source, destination, instanceID); - } + this->Connect_datalayer(source, destination, instanceID); return relationUID; }; -void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, const IPropertyProvider *destination) const +void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer) const { + if (source == nullptr) + { + mitkThrow() << "Error. Source is invalid. Cannot disconnect."; + } + + if (destination == nullptr) + { + mitkThrow() << "Error. Destination is invalid. Cannot disconnect."; + } + try { const auto relationUIDs = this->GetRelationUIDs(source, destination); for (const auto relUID: relationUIDs) { - this->Disconnect(source, relUID); + this->Disconnect(source, relUID, layer); } } catch (const NoPropertyRelationException &) { // nothing to do and no real error in context of disconnect. } }; -void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, RelationUIDType relationUID) const +void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer) const { - auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); + if (source == nullptr) + { + mitkThrow() << "Error. Source is invalid. Cannot disconnect."; + } - if (instanceID != NULL_INSTANCE_ID()) + if (layer == RelationType::Data || layer == RelationType::Complete) { - this->Disconnect_datalayer(source, instanceID); + this->Disconnect_datalayer(source, relationUID); + } + auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); + if ((layer == RelationType::ID || layer == RelationType::Complete) && instanceID != NULL_INSTANCE_ID()) + { auto instancePrefix = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID)); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (key.find(instancePrefix) == 0) { source->RemoveProperty(key); } } } }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::CreateRelationUID() { UIDGenerator generator; return generator.GetUID(); }; /**This mutex is used to guard mitk::PropertyRelationRuleBase::CreateNewRelationInstance by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it is an implementation detail. */ std::mutex relationCreationLock; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::CreateNewRelationInstance( IPropertyOwner *source, const RelationUIDType &relationUID) const { std::lock_guard guard(relationCreationLock); ////////////////////////////////////// // Get all existing instanc IDs std::vector instanceIDs; InstanceIDType newID = "1"; auto destRegExStr = PropertyKeyPathToPropertyRegEx(this->GetRootKeyPath().AddAnyElement().AddElement("relationUID")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { if (instance_matches.size()>1) { instanceIDs.push_back(std::stoi(instance_matches[1])); } } } ////////////////////////////////////// // Get new ID std::sort(instanceIDs.begin(), instanceIDs.end()); if (!instanceIDs.empty()) { newID = std::to_string(instanceIDs.back() + 1); } ////////////////////////////////////// // reserve new ID auto relUIDKey = PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(newID).AddElement("relationUID")); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); return newID; }; itk::LightObject::Pointer mitk::PropertyRelationRuleBase::InternalClone() const { return Superclass::InternalClone(); }; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByPropertyName(const std::string propName) { auto proppath = PropertyNameToPropertyKeyPath(propName); auto ref = GetRootKeyPath(); if (proppath.GetSize() < 3 || !(proppath.GetFirstNode() == ref.GetFirstNode()) || !(proppath.GetNode(1) == ref.GetNode(1))) { mitkThrow() << "Property name is not for a RII property or containes no instance ID. Wrong name: " << propName; } return proppath.GetNode(2).name; }; mitk::PropertyRelationRuleBase::RuleIDType mitk::PropertyRelationRuleBase::GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const { if (!source) { mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; } auto path = GetRootKeyPath().AddElement(instanceID).AddElement("ruleID"); auto name = PropertyKeyPathToPropertyName(path); const auto prop = source->GetConstProperty(name); std::string result; if (prop.IsNotNull()) { result = prop->GetValueAsString(); } if (result.empty()) { mitkThrowException(NoPropertyRelationException) << "Error. Source has no property relation with the passed instance ID. Instance ID: " << instanceID; } return result; }; +std::string mitk::PropertyRelationRuleBase::GetDestinationUIDByInstanceID(const IPropertyProvider* source, + const InstanceIDType& instanceID) const +{ + if (!source) + { + mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; + } + + auto path = GetRootKeyPath().AddElement(instanceID).AddElement("destinationUID"); + auto name = PropertyKeyPathToPropertyName(path); + + const auto prop = source->GetConstProperty(name); + + std::string result; + + if (prop.IsNotNull()) + { + result = prop->GetValueAsString(); + } + + return result; +}; + + namespace mitk { /** * \brief Predicate used to wrap rule checks. * * \ingroup DataStorage */ class NodePredicateRuleFunction : public NodePredicateBase { public: using FunctionType = std::function; mitkClassMacro(NodePredicateRuleFunction, NodePredicateBase) mitkNewMacro2Param(NodePredicateRuleFunction, const FunctionType &, PropertyRelationRuleBase::ConstPointer) ~NodePredicateRuleFunction() override = default; bool CheckNode(const mitk::DataNode *node) const override { if (!node) { return false; } return m_Function(node, m_Rule); }; protected: explicit NodePredicateRuleFunction(const FunctionType &function, PropertyRelationRuleBase::ConstPointer rule) : m_Function(function), m_Rule(rule) { }; FunctionType m_Function; PropertyRelationRuleBase::ConstPointer m_Rule; }; } // namespace mitk mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourceCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSourceCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsDestinationCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetConnectedSourcesDetector() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSource(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourcesDetector( - const IPropertyProvider *destination, RelationType minimalRelation) const + const IPropertyProvider *destination, RelationType exclusiveRelation) const { if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } - auto check = [destination, minimalRelation](const mitk::IPropertyProvider *node, + auto check = [destination, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { - return rule->HasRelation(node, destination) >= minimalRelation; + return rule->HasRelation(node, destination, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationsDetector( - const IPropertyProvider *source, RelationType minimalRelation) const + const IPropertyProvider *source, RelationType exclusiveRelation) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } - auto check = [source, minimalRelation](const mitk::IPropertyProvider *node, + auto check = [source, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { - return rule->HasRelation(source, node) >= minimalRelation; + return rule->HasRelation(source, node, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationDetector( const IPropertyProvider *source, RelationUIDType relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto relUIDs = this->GetExistingRelations(source); if (std::find(relUIDs.begin(), relUIDs.end(), relationUID) == relUIDs.end()) { mitkThrow() << "Error. Passed relationUID does not identify a relation instance of the passed source for this rule instance."; }; auto check = [source, relationUID](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { try { auto relevantUIDs = rule->GetRelationUIDs(source, node); for (const auto& aUID : relevantUIDs) { if (aUID == relationUID) { return true; } } } catch(const NoPropertyRelationException &) { return false; } return false; }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; diff --git a/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp b/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp index 175829e218..27e7a0aac2 100644 --- a/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp +++ b/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp @@ -1,376 +1,420 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include "mitkSourceImageRelationRule.h" #include "mitkPropertyNameHelper.h" #include "mitkStringProperty.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkDataNode.h" +#include "mitkIdentifiable.h" +std::string mitk::SourceImageRelationRule::GenerateRuleID(const std::string& purpose) const +{ + return "SourceImageRelation " + purpose; +} bool mitk::SourceImageRelationRule::IsAbstract() const { return m_PurposeTag.empty(); }; bool mitk::SourceImageRelationRule::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID() || (IsAbstract() && ruleID.find("SourceImageRelation ") == 0); }; mitk::SourceImageRelationRule::RuleIDType mitk::SourceImageRelationRule::GetRuleID() const { - return "SourceImageRelation " + m_PurposeTag; + return this->GenerateRuleID(m_PurposeTag); }; std::string mitk::SourceImageRelationRule::GetDisplayName() const { return m_DisplayName; }; std::string mitk::SourceImageRelationRule::GetSourceRoleName() const { return m_SourceRole; }; std::string mitk::SourceImageRelationRule::GetDestinationRoleName() const { return m_DestinationRole; }; bool mitk::SourceImageRelationRule::IsDestinationCandidate(const IPropertyProvider *owner) const { auto node = dynamic_cast(owner); auto image = nullptr != node ? dynamic_cast(node->GetData()) : dynamic_cast(owner); return image != nullptr; } mitk::SourceImageRelationRule::RelationUIDType mitk::SourceImageRelationRule::Connect(Image *source, const Image *destination) const { return Superclass::Connect(source, destination); }; mitk::SourceImageRelationRule::SourceImageRelationRule() : m_PurposeTag(""), m_DisplayName("Abstract image to image relation"), m_SourceRole("derived data"), m_DestinationRole("source image") {}; mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag) : SourceImageRelationRule(purposeTag, purposeTag + " relation"){}; mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName) : SourceImageRelationRule( purposeTag, displayName, "derived data", "source image"){}; mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole) : m_PurposeTag(purposeTag), m_DisplayName(displayName), m_SourceRole(sourceRole), m_DestinationRole(destinationRole){}; -mitk::SourceImageRelationRule::InstanceIDVectorType mitk::SourceImageRelationRule::GetInstanceID_datalayer( - const IPropertyProvider * source, const IPropertyProvider * destination) const +mitk::SourceImageRelationRule::DataRelationUIDVectorType +mitk::SourceImageRelationRule::GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const { - InstanceIDVectorType result; + DataRelationUIDVectorType result; - auto relevantReferenceIndices = GetReferenceSequenceIndices(source, destination); + auto relevantIndicesAndRuleIDs = GetReferenceSequenceIndices(source, destination, instances_IDLayer); - auto itemRegExStr = this->GetRIIPropertyRegEx("SourceImageSequenceItem"); - auto regEx = std::regex(itemRegExStr); + auto itemRIIRegExStr = this->GetRIIPropertyRegEx("SourceImageSequenceItem"); + auto regEx = std::regex(itemRIIRegExStr); //workaround until T24729 is done. Please remove if T24728 is done auto keys = PropertyRelationRuleBase::GetPropertyKeys(source); //end workaround for T24729 - for (const auto &key : keys) + for (const auto &indexNRule : relevantIndicesAndRuleIDs) { - if (std::regex_match(key, regEx)) + bool relationCoveredByRII = false; + for (const auto& key : keys) { - auto sequItemProp = source->GetConstProperty(key); - if (sequItemProp.IsNotNull()) + if (std::regex_match(key, regEx)) { - auto finding = std::find(std::cbegin(relevantReferenceIndices), std::cend(relevantReferenceIndices), sequItemProp->GetValueAsString()); - if (finding != std::cend(relevantReferenceIndices)) + auto sequItemProp = source->GetConstProperty(key); + if (sequItemProp.IsNotNull() && sequItemProp->GetValueAsString() == std::to_string(indexNRule.first)) { + relationCoveredByRII = true; auto instanceID = GetInstanceIDByPropertyName(key); auto ruleID = GetRuleIDByInstanceID(source, instanceID); if (this->IsSupportedRuleID(ruleID)) { - result.push_back(instanceID); + result.emplace_back(this->GetRelationUIDByInstanceID(source, instanceID), ruleID); } } } } + + if (!relationCoveredByRII) + { + PropertyKeyPath referencedInstanceUIDs; + referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddSelection("2112", indexNRule.first).AddElement("0008").AddElement("1155"); + + std::string ruleID = ""; + if (!indexNRule.second.empty()) + { + ruleID = this->GenerateRuleID(indexNRule.second); + } + + result.emplace_back(PropertyKeyPathToPropertyName(referencedInstanceUIDs), ruleID); + } } return result; }; -std::vector mitk::SourceImageRelationRule::GetReferenceSequenceIndices(const IPropertyProvider * source, - const IPropertyProvider * destination) const +std::vector > mitk::SourceImageRelationRule::GetReferenceSequenceIndices(const IPropertyProvider * source, + const IPropertyProvider * destination, InstanceIDVectorType ignoreInstances) const { - std::vector result; + std::vector > result; BaseProperty::ConstPointer destInstanceUIDProp; + std::string destinationUID = ""; if (destination) { destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); if (destInstanceUIDProp.IsNull()) { return result; } + + auto identifiable = dynamic_cast(destination); + + if (!identifiable) + { //This check and pass through to data is needed due to solve T25711. See Task for more information. + //This could be removed at the point we can get rid of DataNodes or they get realy transparent. + auto node = dynamic_cast(destination); + if (node && node->GetData()) + { + identifiable = dynamic_cast(node->GetData()); + } + } + if (identifiable) + { + destinationUID= identifiable->GetUID(); + } + } + + std::vector ignoreItemIndices; + for (const auto& iID : ignoreInstances) + { + auto sourceImageRefPath = GetRootKeyPath().AddElement(iID).AddElement("SourceImageSequenceItem"); + auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath)); + + if (imageRefProp.IsNotNull()) + { + ignoreItemIndices.emplace_back(imageRefProp->GetValueAsString()); + } } PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); - auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);; + auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs); auto regEx = std::regex(sourceRegExStr); std::vector keys; //workaround until T24729 is done. Please remove if T24728 is done keys = PropertyRelationRuleBase::GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto refUIDProp = source->GetConstProperty(key); if (destination==nullptr || *refUIDProp == *destInstanceUIDProp) { auto currentKeyPath = PropertyNameToPropertyKeyPath(key); auto currentKeyPathSelection = currentKeyPath.GetNode(2).selection; - PropertyKeyPath purposePath; - purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", currentKeyPathSelection).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); - auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); - if (this->IsAbstract() || (purposeProp.IsNotNull() && purposeProp->GetValueAsString() == this->m_PurposeTag)) + auto finding = std::find(ignoreItemIndices.begin(), ignoreItemIndices.end(), std::to_string(currentKeyPathSelection)); + if (finding == ignoreItemIndices.end()) { - result.push_back(std::to_string(currentKeyPathSelection)); + PropertyKeyPath purposePath; + purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", currentKeyPathSelection).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); + auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); + std::string currentPurpose = ""; + if (purposeProp.IsNotNull()) + { + currentPurpose = purposeProp->GetValueAsString(); + } + if (this->IsAbstract() || (purposeProp.IsNotNull() && currentPurpose == this->m_PurposeTag)) + { + result.emplace_back(currentKeyPathSelection, currentPurpose); + } } } } } return result; }; -bool mitk::SourceImageRelationRule::HasImplicitDataRelation(const IPropertyProvider * source, - const IPropertyProvider * destination) const -{ - auto relevantReferences = GetReferenceSequenceIndices(source, destination); - - if (this->IsAbstract()) - { - return !relevantReferences.empty(); - } - else - { - for (auto referenceIndex : relevantReferences) - { - PropertyKeyPath purposePath; - purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", std::stoi(referenceIndex)).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); - auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); - - if (purposeProp.IsNotNull() && purposeProp->GetValueAsString() == this->m_PurposeTag) - { - return true; - } - } - } - - return false; -}; - - /**This mutex is used to guard mitk::SourceImageRelationRule::CreateNewSourceImageSequenceItem by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it is an implementation detail. */ namespace { std::mutex sequenceItemCreationLock; } mitk::PropertyKeyPath::ItemSelectionIndex mitk::SourceImageRelationRule::CreateNewSourceImageSequenceItem( IPropertyOwner *source) const { std::lock_guard guard(sequenceItemCreationLock); ////////////////////////////////////// // Get all existing sequence items std::vector instanceIDs; PropertyKeyPath::ItemSelectionIndex newID = 0; PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); auto regExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs); auto regEx = std::regex(regExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { if (instance_matches.size()>1) { instanceIDs.push_back(std::stoi(instance_matches[1])); } } } ////////////////////////////////////// // Get new ID std::sort(instanceIDs.begin(), instanceIDs.end()); if (!instanceIDs.empty()) { newID = instanceIDs.back()+1; } ////////////////////////////////////// // reserve new ID PropertyKeyPath newSourceImageSequencePath; newSourceImageSequencePath.AddElement("DICOM").AddElement("0008").AddSelection("2112",newID).AddElement("0008").AddElement("1155"); auto newKey = PropertyKeyPathToPropertyName(newSourceImageSequencePath); source->SetProperty(newKey, mitk::TemporoSpatialStringProperty::New("reserved slot for source image sequence")); return newID; }; void mitk::SourceImageRelationRule::Connect_datalayer(IPropertyOwner * source, const IPropertyProvider * destination, const InstanceIDType & instanceID) const { auto destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008,0x0018)); auto destClassUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0016)); if (destInstanceUIDProp.IsNotNull() && destClassUIDProp.IsNotNull()) { auto existingRefs = this->GetReferenceSequenceIndices(source, destination); std::string newSelectionIndexStr; if (!existingRefs.empty()) { - newSelectionIndexStr = existingRefs[0]; + newSelectionIndexStr = std::to_string(existingRefs[0].first); } else { PropertyKeyPath::ItemSelectionIndex newSelectionIndex = CreateNewSourceImageSequenceItem(source); PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1155"); source->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), destInstanceUIDProp->Clone()); PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1150"); source->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), destClassUIDProp->Clone()); PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); source->SetProperty(PropertyKeyPathToPropertyName(purposePath), StringProperty::New(m_PurposeTag)); newSelectionIndexStr = std::to_string(newSelectionIndex); } auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); source->SetProperty(PropertyKeyPathToPropertyName(sourceImageRefPath), StringProperty::New(newSelectionIndexStr).GetPointer()); } else { MITK_DEBUG << "Cannot connect SourceImageRelationRule on data layer. Passed destination does not have properties for DICOM SOP Instance UIDs(0x0008, 0x0018) and DICOM SOP Class UID(0x0008, 0x0016)"; } }; -void mitk::SourceImageRelationRule::Disconnect_datalayer(IPropertyOwner * source, const InstanceIDType & instanceID) const +void mitk::SourceImageRelationRule::Disconnect_datalayer(IPropertyOwner * source, const RelationUIDType & relationUID) const { - auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); - auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath)); + std::string deletedImageRefSequenceIndexStr = ""; - if (imageRefProp.IsNotNull()) + if (relationUID.find("DICOM") == 0) + { //relation that is purly data based. + auto currentKeyPath = PropertyNameToPropertyKeyPath(relationUID); + deletedImageRefSequenceIndexStr = std::to_string(currentKeyPath.GetNode(2).selection); + } + else { - auto deletedImageRefSequenceIndex = imageRefProp->GetValueAsString(); + auto sourceImageRefPath = GetRootKeyPath().AddElement(this->GetInstanceIDByRelationUID(source,relationUID)).AddElement("SourceImageSequenceItem"); + auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath)); + if (imageRefProp.IsNotNull()) + { + deletedImageRefSequenceIndexStr = imageRefProp->GetValueAsString(); + } + } + if(!deletedImageRefSequenceIndexStr.empty()) + { + auto deletedImageRefSequenceIndex = std::stoull(deletedImageRefSequenceIndexStr); auto refs = GetReferenceSequenceIndices(source); std::sort(refs.begin(), refs.end()); - for (auto refIndexStr : refs) + for (auto refIndex : refs) { - auto refIndex = std::stoi(refIndexStr); - - if (refIndex >= std::stoi(deletedImageRefSequenceIndex)) + if (refIndex.first >= deletedImageRefSequenceIndex) { PropertyKeyPath refDICOMDataPath; - refDICOMDataPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", refIndex); + refDICOMDataPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", refIndex.first); auto prefix = PropertyKeyPathToPropertyName(refDICOMDataPath); PropertyKeyPath refRelDataPath = GetRootKeyPath().AddAnyElement().AddElement("SourceImageSequenceItem");; - auto regEx = std::regex(PropertyKeyPathToPropertyRegEx(refRelDataPath)); + auto riiRegEx = std::regex(PropertyKeyPathToPropertyRegEx(refRelDataPath)); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (key.find(prefix) == 0) { //its a relevant DICOM property delete or update - if (refIndexStr != deletedImageRefSequenceIndex) + if (refIndex.first != deletedImageRefSequenceIndex) { //reindex to close the gap in the dicom sequence. auto newPath = PropertyNameToPropertyKeyPath(key); - newPath.GetNode(2).selection = refIndex - 1; + newPath.GetNode(2).selection = refIndex.first - 1; source->SetProperty(PropertyKeyPathToPropertyName(newPath), source->GetNonConstProperty(key)); } //remove old/outdated data layer information source->RemoveProperty(key); - - auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); } - if (std::regex_match(key, regEx)) - { + if (std::regex_match(key, riiRegEx)) + { //it is a relevant RII property, remove it or update it. auto imageSequenceItemProp = source->GetConstProperty(key); - if (imageSequenceItemProp->GetValueAsString() == std::to_string(refIndex)) + if (imageSequenceItemProp->GetValueAsString() == deletedImageRefSequenceIndexStr) + { + source->RemoveProperty(key); + } + else if (imageSequenceItemProp->GetValueAsString() == std::to_string(refIndex.first)) { - //its a relevant data property of the relation rule. - source->SetProperty(key, StringProperty::New(std::to_string(refIndex - 1))); + //its a relevant data property of the relation rule reindex it. + source->SetProperty(key, StringProperty::New(std::to_string(refIndex.first - 1))); } } } } } } }; itk::LightObject::Pointer mitk::SourceImageRelationRule::InternalClone() const { itk::LightObject::Pointer result = Self::New(this->m_PurposeTag, this->m_DisplayName, this->m_SourceRole, this->m_DestinationRole).GetPointer(); return result; }; diff --git a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp index 04768c9a2d..1be7a6b47e 100644 --- a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp +++ b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp @@ -1,915 +1,1101 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPropertyRelationRuleBase.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include /** This class is used to test PropertyRelationRuleBase and get access to internals where needed to test them as well. */ namespace mitk { class TestRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(TestRule, mitk::PropertyRelationRuleBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; RuleIDType GetRuleID() const override { if (m_AbstractMode) { return "TestRule"; } else { return "TestRule_type1"; } }; std::string GetDisplayName() const override { return "TestDisplayName"; } std::string GetSourceRoleName() const override { return "source role"; } std::string GetDestinationRoleName() const override { return "destination role"; } bool m_AbstractMode; bool IsAbstract() const override { return m_AbstractMode; } using Superclass::GetRootKeyPath; using Superclass::Connect; protected: TestRule() : m_AbstractMode(false) { }; ~TestRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; bool IsSupportedRuleID(const RuleIDType& ruleID) const override { if (m_AbstractMode) { return ruleID.find(this->GetRuleID()) == 0; } else { return Superclass::IsSupportedRuleID(ruleID); } }; - InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, - const IPropertyProvider *destination) const override + DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override { - InstanceIDVectorType result; + DataRelationUIDVectorType result; - auto destProp = destination->GetConstProperty("name"); + mitk::BaseProperty::ConstPointer destProp; + + if (destination != nullptr) + { + destProp = destination->GetConstProperty("name"); + } if (destProp.IsNotNull()) { auto destRegExStr = PropertyKeyPathToPropertyRegEx(Superclass::GetRootKeyPath().AddAnyElement().AddElement("dataHandle")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto keys = source->GetPropertyKeys(); for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto sourceProp = source->GetConstProperty(key); if (sourceProp->GetValueAsString() == destProp->GetValueAsString()) { if (instance_matches.size()>1) { - result.push_back(instance_matches[1]); + auto finding = std::find(instances_IDLayer.begin(), instances_IDLayer.end(), instance_matches[1]); + if (finding == instances_IDLayer.end()) + { + result.emplace_back(this->GetRelationUIDByInstanceID(source, instance_matches[1]), this->GetRuleIDByInstanceID(source, instance_matches[1])); + } } } } } } - return result; - }; - bool HasImplicitDataRelation(const IPropertyProvider *source, - const IPropertyProvider *destination) const override - { - auto destProp = destination->GetConstProperty("name"); - auto sourceProp = source->GetConstProperty("referencedName"); + if (result.empty() && instances_IDLayer.empty()) + { + auto refNameProp = source->GetConstProperty("referencedName"); + if (refNameProp.IsNotNull() && (destProp.IsNull() || destProp->GetValueAsString() == refNameProp->GetValueAsString())) + { + result.emplace_back(refNameProp->GetValueAsString(),""); + } + } - return destProp.IsNotNull() && sourceProp.IsNotNull() && - destProp->GetValueAsString() == sourceProp->GetValueAsString(); + return result; }; void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override { auto destProp = destination->GetConstProperty("name"); source->SetProperty("referencedName", StringProperty::New(destProp->GetValueAsString())); source->SetProperty( PropertyKeyPathToPropertyName(Superclass::GetRootKeyPath().AddElement(instanceID).AddElement("dataHandle")), StringProperty::New(destProp->GetValueAsString())); }; - void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType & /*instanceID*/) const override + void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const override { source->RemoveProperty("referencedName"); + try + { + auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); + source->RemoveProperty( + PropertyKeyPathToPropertyName(Superclass::GetRootKeyPath().AddElement(instanceID).AddElement("dataHandle"))); + } + catch(...) + { } }; }; } // namespace mitk class mitkPropertyRelationRuleBaseTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPropertyRelationRuleBaseTestSuite); MITK_TEST(GetRootKeyPath); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); MITK_TEST(GetDestinationsDetector); MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); + MITK_TEST(Disconnect_partial_ID); + MITK_TEST(Disconnect_partial_Data); MITK_TEST(Connect_abstract); MITK_TEST(Disconnect_abstract); CPPUNIT_TEST_SUITE_END(); private: mitk::TestRule::Pointer rule; mitk::TestRule::Pointer abstractRule; mitk::DataNode::Pointer unRelated; mitk::PointSet::Pointer unRelated_1_data; mitk::DataNode::Pointer source_implicit_1; mitk::DataNode::Pointer source_data_1; mitk::DataNode::Pointer source_idOnly_1; mitk::DataNode::Pointer source_1; mitk::DataNode::Pointer source_multi; mitk::DataNode::Pointer source_otherRule; mitk::DataNode::Pointer source_otherTypeRule; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1; mitk::PointSet::Pointer dest_1_data; mitk::DataNode::Pointer dest_2; mitk::PointSet::Pointer dest_2_data; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } public: void setUp() override { rule = mitk::TestRule::New(); abstractRule = mitk::TestRule::New(); abstractRule->m_AbstractMode = true; unRelated = mitk::DataNode::New(); unRelated->SetName("unRelated"); unRelated_1_data = mitk::PointSet::New(); unRelated->SetData(unRelated_1_data); dest_1 = mitk::DataNode::New(); dest_1->SetName("dest_1"); dest_1_data = mitk::PointSet::New(); dest_1->SetData(dest_1_data); dest_2 = mitk::DataNode::New(); dest_2->SetName("dest_2"); dest_2_data = mitk::PointSet::New(); dest_2->SetData(dest_2_data); source_implicit_1 = mitk::DataNode::New(); source_implicit_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); source_idOnly_1 = mitk::DataNode::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); - source_data_1 = source_implicit_1->Clone(); + source_data_1 = mitk::DataNode::New(); + source_data_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.dataHandle"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid10"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); - source_1 = source_data_1->Clone(); + source_1 = mitk::DataNode::New(); + source_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid3"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); + name = "MITK.Relations.1.dataHandle"; + source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid8"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); - source_multi = source_1->Clone(); + source_multi = mitk::DataNode::New(); + source_multi->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid4"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); + name = "MITK.Relations.1.dataHandle"; + source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.4.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid5")); name = "MITK.Relations.4.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.4.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid6")); name = "MITK.Relations.2.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("unknown")); name = "MITK.Relations.2.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_otherRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherTypeRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); } void tearDown() override {} void GetRootKeyPath() { mitk::PropertyKeyPath ref; ref.AddElement("MITK").AddElement("Relations"); CPPUNIT_ASSERT(mitk::PropertyRelationRuleBase::GetRootKeyPath() == ref); } void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); - CPPUNIT_ASSERT(!rule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(rule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(rule->IsSource(source_data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); CPPUNIT_ASSERT(rule->IsSource(source_multi)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherTypeRule)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); - CPPUNIT_ASSERT(!abstractRule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_multi)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherTypeRule)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_otherTypeRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated)); + CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1)); + CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1)); + CPPUNIT_ASSERT(!rule->HasRelation(source_otherTypeRule, dest_1)); + + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1)); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2)); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2)); + CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, unRelated)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(unRelated, dest_1)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherRule, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherTypeRule).empty()); - CPPUNIT_ASSERT(rule->GetExistingRelations(source_implicit_1).empty()); - auto uids = rule->GetExistingRelations(source_idOnly_1); + auto uids = rule->GetExistingRelations(source_implicit_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == dest_1->GetName()); + + uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = rule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); uids = rule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); - CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_implicit_1).empty()); + + uids = abstractRule->GetExistingRelations(source_implicit_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == dest_1->GetName()); uids = abstractRule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = abstractRule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); uids = abstractRule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); uids = abstractRule->GetExistingRelations(source_otherTypeRule); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid9"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherTypeRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherTypeRule, dest_1).front() == "uid9"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); uids = abstractRule->GetRelationUIDs(source_data_1, dest_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_2); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, dest_1).front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); auto predicate2 = abstractRule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated)); - CPPUNIT_ASSERT(!predicate2->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate2->CheckNode(source_otherTypeRule)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); - predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); - CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); - predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); - predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); + + CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); + + predicate = rule->GetSourcesDetector(dest_2); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); - predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); + predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); + + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); + + predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); + + CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); + predicate = abstractRule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); } void GetDestinationsDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationsDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetDestinationsDetector(source_otherRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Complete); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); + predicate = + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); predicate = - rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); - CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = - rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); + predicate = + rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Complete); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); } void GetDestinationDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationDetector(nullptr, "uid1"), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", rule->GetDestinationDetector(source_1, "invalid uid"), itk::ExceptionObject); auto predicate = rule->GetDestinationDetector(source_1, "uid3"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationDetector(source_multi, "uid5"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_implicit_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); // check upgrade of an data connection - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_data_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); std::string name = "MITK.Relations.1.destinationUID"; auto prop = source_data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1_data->GetUID()); // check actualization of an id only connection + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_idOnly_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.dataHandle"; prop = source_idOnly_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information not stored.", prop->GetValueAsString() == dest_1->GetName()); prop = source_idOnly_1->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == dest_1->GetName()); // check actualization of an existing connection rule->Connect(source_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); // check new connection auto newConnectUID = rule->Connect(source_multi, unRelated); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, unRelated) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, unRelated, mitk::PropertyRelationRuleBase::RelationType::Complete)); name = "MITK.Relations.5.dataHandle"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); prop = source_multi->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); name = "MITK.Relations.5.destinationUID"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated_1_data->GetUID()); auto storedRelationUIDs = rule->GetRelationUIDs(source_multi, unRelated); CPPUNIT_ASSERT_MESSAGE( "Relation uid was not stored for given source and destination.", storedRelationUIDs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Incorrect Relation uid was stored.", storedRelationUIDs[0] == newConnectUID); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT_MESSAGE("Data property was not removed.", !source_1->GetProperty("referencedName")); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); rule->Disconnect(source_1, dest_2); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(this->hasRelationProperties(source_1)); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.",this->hasRelationProperties(source_1, "2")); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_multi, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", this->hasRelationProperties(source_otherTypeRule, "1")); } + void Disconnect_partial_ID() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->Disconnect(nullptr, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(source_1, nullptr, mitk::PropertyRelationRuleBase::RelationType::ID), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(nullptr, "uid", mitk::PropertyRelationRuleBase::RelationType::ID), + itk::ExceptionObject); + + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + rule->Disconnect(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); + + rule->Disconnect(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + + rule->Disconnect(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + rule->Disconnect(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); + CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", this->hasRelationProperties(source_1, "2")); + } + + void Disconnect_partial_Data() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->Disconnect(nullptr, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(source_1, nullptr, mitk::PropertyRelationRuleBase::RelationType::Data), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(nullptr, "uid", mitk::PropertyRelationRuleBase::RelationType::Data), + itk::ExceptionObject); + + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + rule->Disconnect(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); + + rule->Disconnect(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + + rule->Disconnect(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + rule->Disconnect(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + } + void Connect_abstract() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(source_1, nullptr), itk::ExceptionObject); } void Disconnect_abstract() { - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); abstractRule->Disconnect(source_1, dest_2); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_1); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_multi, dest_1); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherTypeRule, "1")); - } }; MITK_TEST_SUITE_REGISTRATION(mitkPropertyRelationRuleBase) diff --git a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp index 2494c9c792..006153952b 100644 --- a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp +++ b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp @@ -1,880 +1,944 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSourceImageRelationRule.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkPropertyNameHelper.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkPropertyNameHelper.h" #include class mitkSourceImageRelationRuleTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSourceImageRelationRuleTestSuite); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); MITK_TEST(GetDestinationsDetector); MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); MITK_TEST(Connect_abstract); MITK_TEST(Disconnect_abstract); CPPUNIT_TEST_SUITE_END(); private: mitk::SourceImageRelationRule::Pointer rule; mitk::SourceImageRelationRule::Pointer abstractRule; mitk::Image::Pointer unRelated; mitk::DataNode::Pointer unRelated_Node; mitk::Image::Pointer source_implicit_1; mitk::DataNode::Pointer source_implicit_1_Node; - mitk::Image::Pointer source_data_1; - mitk::DataNode::Pointer source_data_1_Node; + mitk::Image::Pointer source_Data_1; + mitk::DataNode::Pointer source_Data_1_Node; mitk::Image::Pointer source_idOnly_1; mitk::DataNode::Pointer source_idOnly_1_Node; mitk::Image::Pointer source_1; mitk::DataNode::Pointer source_1_Node; mitk::Image::Pointer source_otherRule; mitk::DataNode::Pointer source_otherRule_Node; mitk::Image::Pointer source_otherPurpose; mitk::DataNode::Pointer source_otherPurpose_Node; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1_Node; mitk::Image::Pointer dest_1; mitk::DataNode::Pointer dest_2_Node; mitk::Image::Pointer dest_2; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } std::vector GetReferenceSequenceIndices(const mitk::IPropertyProvider * source, const mitk::IPropertyProvider * destination) const { std::vector result; auto destInstanceUIDProp = destination->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); if (destInstanceUIDProp.IsNull()) { return result; } mitk::PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);; auto regEx = std::regex(sourceRegExStr); std::vector keys; //workaround until T24729 is done. Please remove if T24728 is done keys = source->GetPropertyKeys(); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto refUIDProp = source->GetConstProperty(key); if (*refUIDProp == *destInstanceUIDProp) { mitk::PropertyKeyPath finding = mitk::PropertyNameToPropertyKeyPath(key); result.push_back(std::to_string(finding.GetNode(2).selection)); } } } return result; }; void SetDICOMReferenceInfo(mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); owner->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), mitk::TemporoSpatialStringProperty::New(instanceUID)); mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); owner->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), mitk::TemporoSpatialStringProperty::New(classUID)); mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); owner->SetProperty(PropertyKeyPathToPropertyName(purposePath), mitk::TemporoSpatialStringProperty::New(purpose)); } bool IsCorrectDICOMReference(const mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) const { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); auto prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath)); if (prop->GetValueAsString() != instanceUID) { return false; } mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refClassUIDPath)); if (prop->GetValueAsString() != classUID) { return false; } mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); if (prop->GetValueAsString() != purpose) { return false; } return true; } public: void setUp() override { auto instanceUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018); auto classUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0016); rule = mitk::SourceImageRelationRule::New("Test"); abstractRule = mitk::SourceImageRelationRule::New(); unRelated = mitk::Image::New(); unRelated->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("unRelated")); unRelated->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); unRelated_Node = mitk::DataNode::New(); unRelated_Node->SetData(unRelated); dest_1_Node = mitk::DataNode::New(); dest_1_Node->SetName("dest_1"); dest_1 = mitk::Image::New(); dest_1->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_1")); dest_1->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); dest_1_Node->SetData(dest_1); dest_2_Node = mitk::DataNode::New(); dest_2_Node->SetName("dest_2"); dest_2 = mitk::Image::New(); dest_2->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_2")); dest_2->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); dest_2_Node->SetData(dest_2); source_implicit_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_implicit_1, "dest_1", "image", "Test", 0); source_implicit_1_Node = mitk::DataNode::New(); source_implicit_1_Node->SetData(source_implicit_1); source_idOnly_1 = mitk::Image::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_idOnly_1_Node = mitk::DataNode::New(); source_idOnly_1_Node->SetData(source_idOnly_1); - source_data_1 = mitk::Image::New(); - SetDICOMReferenceInfo(source_data_1, "dest_1", "image", "Test", 0); - SetDICOMReferenceInfo(source_data_1, "dest_2", "image", "otherpurpose", 1); + source_Data_1 = mitk::Image::New(); + SetDICOMReferenceInfo(source_Data_1, "dest_1", "image", "Test", 0); + SetDICOMReferenceInfo(source_Data_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2")); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.ruleID"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10")); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10")); name = "MITK.Relations.2.SourceImageSequenceItem"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); name = "MITK.Relations.2.ruleID"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); - source_data_1_Node = mitk::DataNode::New(); - source_data_1_Node->SetData(source_data_1); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); + source_Data_1_Node = mitk::DataNode::New(); + source_Data_1_Node->SetData(source_Data_1); source_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_1, "dest_1", "image", "Test", 0); SetDICOMReferenceInfo(source_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid3")); name = "MITK.Relations.1.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid8")); name = "MITK.Relations.2.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_2->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); name = "MITK.Relations.2.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); source_1_Node = mitk::DataNode::New(); source_1_Node->SetData(source_1); source_otherRule = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherRule_Node = mitk::DataNode::New(); source_otherRule_Node->SetData(source_otherRule); source_otherPurpose = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); source_otherPurpose_Node = mitk::DataNode::New(); source_otherPurpose_Node->SetData(source_otherPurpose); } void tearDown() override {} void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1_Node)); CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1)); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); - CPPUNIT_ASSERT(!rule->IsSource(source_implicit_1)); - CPPUNIT_ASSERT(rule->IsSource(source_data_1)); + CPPUNIT_ASSERT(rule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(rule->IsSource(source_Data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose)); - CPPUNIT_ASSERT(!rule->IsSource(source_implicit_1_Node)); - CPPUNIT_ASSERT(rule->IsSource(source_data_1_Node)); + CPPUNIT_ASSERT(rule->IsSource(source_implicit_1_Node)); + CPPUNIT_ASSERT(rule->IsSource(source_Data_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_1_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose_Node)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); - CPPUNIT_ASSERT(!abstractRule->IsSource(source_implicit_1)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated_Node)); - CPPUNIT_ASSERT(!abstractRule->IsSource(source_implicit_1_Node)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1_Node)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1_Node)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1_Node)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose_Node)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_otherPurpose, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); - - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!rule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); + + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherPurpose).empty()); - CPPUNIT_ASSERT(rule->GetExistingRelations(source_implicit_1).empty()); - auto uids = rule->GetExistingRelations(source_idOnly_1); + auto uids = rule->GetExistingRelations(source_implicit_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); + + uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); - uids = rule->GetExistingRelations(source_data_1); + uids = rule->GetExistingRelations(source_Data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); - CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); - CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_implicit_1).empty()); + + uids = abstractRule->GetExistingRelations(source_implicit_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); uids = abstractRule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); - uids = abstractRule->GetExistingRelations(source_data_1); + uids = abstractRule->GetExistingRelations(source_Data_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); uids = abstractRule->GetExistingRelations(source_otherPurpose); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid9"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherPurpose, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); - CPPUNIT_ASSERT(rule->GetRelationUIDs(source_data_1, dest_1).front() == "uid2"); + CPPUNIT_ASSERT(rule->GetRelationUIDs(source_Data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherPurpose, dest_1).front() == "uid9"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); - uids = abstractRule->GetRelationUIDs(source_data_1, dest_1); + uids = abstractRule->GetRelationUIDs(source_Data_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_2); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(this->dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); auto predicate2 = abstractRule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate2->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate2->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_1_Node)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_otherPurpose_Node)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); - predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); + + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); - predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); - predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1_Node)); - predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = abstractRule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); } void GetDestinationsDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationsDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetDestinationsDetector(source_otherRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - predicate = rule->GetDestinationsDetector(source_data_1); + predicate = rule->GetDestinationsDetector(source_Data_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Complete); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); + predicate = + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); } void GetDestinationDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationDetector(nullptr, "uid1"), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", rule->GetDestinationDetector(source_1, "invalid uid"), itk::ExceptionObject); auto predicate = rule->GetDestinationDetector(source_1, "uid3"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); rule->Connect(source_implicit_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); auto dcmRefs = GetReferenceSequenceIndices(source_implicit_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_implicit_1, "dest_1", "image", "Test", 0)); - // check upgrade and reuse of an data connection (no new relation should be generated). - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - rule->Connect(source_data_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + // check upgrade and reuse of an Data connection (no new relation should be generated). + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + rule->Connect(source_Data_1, dest_1); + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - auto relUID = rule->GetRelationUIDs(source_data_1, dest_1); + auto relUID = rule->GetRelationUIDs(source_Data_1, dest_1); CPPUNIT_ASSERT(relUID.size() == 1); std::string name = "MITK.Relations.1.destinationUID"; - auto prop = source_data_1->GetProperty(name.c_str()); + auto prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; - prop = source_data_1->GetProperty(name.c_str()); + prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; - prop = source_data_1->GetProperty(name.c_str()); + prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); - dcmRefs = GetReferenceSequenceIndices(source_data_1, dest_1); + dcmRefs = GetReferenceSequenceIndices(source_Data_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); - CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_data_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_Data_1, "dest_1", "image", "Test", 0)); // check actualization of an id only connection rule->Connect(source_idOnly_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); // check actualization of an existing connection rule->Connect(source_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(source_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); // check creation of an new connection rule->Connect(unRelated, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating exting one.", rule->GetExistingRelations(unRelated).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference squence is corrupted. Should be just an index 0.", dcmRefs[0] == "0"); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); // check creation of a 2nd connection of the same purpose rule->Connect(unRelated, dest_2); - CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was not defined.", rule->GetExistingRelations(unRelated).size() == 2); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); name = "MITK.Relations.2.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_2->GetUID()); name = "MITK.Relations.2.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.2.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "1"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_2); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was not defined.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_2", "image", "Test", 1)); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT_MESSAGE("Other relationdata property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT_MESSAGE("Other relationData property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); //check if index correction is correct, when disconnecting rule->Connect(source_1, dest_2); rule->Connect(source_1, unRelated); rule->Disconnect(source_1, dest_2); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "3")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "4")); CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_1 has been removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_2 (other purpose) has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "dest_2", "image", "otherpurpose", 1)); CPPUNIT_ASSERT_MESSAGE("Dicom reference to unRelated has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "unRelated", "image", "Test", 2)); std::string name = "MITK.Relations.4.destinationUID"; auto prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated->GetUID()); name = "MITK.Relations.4.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("SourceImageSequenceItem was not actualized correctly.", prop->GetValueAsString() == "2"); rule->Disconnect(source_otherPurpose, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule purpose was removed.", this->hasRelationProperties(source_otherPurpose, "1")); } void Connect_abstract() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(source_1, nullptr), itk::ExceptionObject); } void Disconnect_abstract() { - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_2); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_1); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); abstractRule->Disconnect(source_otherPurpose, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherPurpose, "1")); } }; MITK_TEST_SUITE_REGISTRATION(mitkSourceImageRelationRule)