Base class for PropertyRelationRules.
Other informations are directly encoded in the documentation of the draft proposal.
Proposal for the interface draft:
lang=c++
/**Base class to standardize/abstract/encapsulate rules and business logic to detect and define
(property/data based) releations in MITK.
Following important definitions must be regarded when using/implementing/specifing rule classes:
- Releation represented by rules are directed relations that point from a source IPropertyOwner (Source)
to a destination IPropertyOwner (Destination).
- A concrete rule class always implements a concrete releation 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 purpuse. One may implement an interim class that manages the mutual
stuff, but the nonabstract rule classes must be one for "DICOM source input image" and one for
"DICOM source mask" etc.
- 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 on relation of a specific rule.
- The deletion of a Destination in the storage does not remove the relation implecitly. It becomes a "zombie" relation
but should still be documented, even if the destination is unkown. The and a (zombie) relation, one has to explicitly
disconnect it.
- Each releation has its on UID (relationID) that can be used to address it.
The basic concept of the rule design is that we have to 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
whicht uses the properties of Source and Destination to deduce if there is a relation of the rule type.
The ID-layer is completly implemented by this based 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.
Reasons for the introduction of the ID-layer are: 1st, data defined releations may be weak (several Destinations are
possible; e.g. DICOM source images may point to several loaded mitk images). But if expicitly a releation was connected
it should be deduceable. 2nd, Checks on the UID are faster then unnecessary data deduction.
Rules use properties (relationID-properties) in order to manage their relationIDs that are stored in the Source.
The relationID-properties follow the following naming schema:
"MITK.relations.<RuleID>.<Index>.[relationID|destinationID|<data-layer-specific>]"
- <RuleID>: The identifier of the concrete rule that sets the property
- <InstanceID>: The unique index of the relations of the rule for the Source. Used to assigne/group the properties to their
relation.
- relationID: The UID of the relation. Set by the ID-layer (so by this class)
- destinationID: The UID of the Destination. Set by the ID-layer (so by this class) if Destination implements IIdentifiable.
- <data-layer-specific>: Information needed by the Data-layer (so derived classes) to find the relationID
*/
class PropertyReleationRuleBase : public itk::LightObject
{
public:
mitkClassMacroItkParent(PropertyReleationRuleBase, itk::LightObject);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
using RuleIDType = std::string;
using RelationIDType = IIdentifiable::UIDType;
using RelationIDVectorType = std::vector<RelationIDType>;
/** 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 "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.*/
}
/** Returns an ID string that identifies the rule class */
virtual RuleIDType GetRuleID() const = 0;
/** Returns a humanreadable string that can be used to described the rule. Must not be unique.*/
virtual std::string GetDesplayName() const = 0;
/** This method checks if owner is eligible to be a Source for the rule. The default implementation returns a
True for every valid IPropertyOwner (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 IPropertyOwner *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 IPropertyOwner (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 IPropertyOwner *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.
@pre owner must be a pointer to a valid IPropertyOwner instance.*/
bool IsSource(const IPropertyOwner *owner) const;
/** Returns the relation type of the passed IPropertyOwner instances.
@pre source must be a pointer to a valid IPropertyOwner instance.
@pre destination must be a pointer to a valid IPropertyOwner instance.
*/
RelationType HasRelation(const IPropertyOwner *source, const IPropertyOwner *destination) const;
/** Returns a vector of IDs for all relations of this rule that are defined for the passed
source (so relations of type Connected_Data and Connected_ID).
*@pre source must be a pointer to a valid IPropertyOwner instance.
*/
RelationIDVectorType GetExistingRelations(const IPropertyOwner *source) const;
/** Returns the relation ID for the passed source and destination. If the passed instances have no
explicit relation (so of type Connected_Data or Connected_ID) no ID can be deduced an exception
will be thrown.
@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 a relation of type Connected_Data or Connected_ID*/
RelationIDType GetRelationID(const IPropertyOwner *source, const IPropertyOwner *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.
Thus all nodes that have relations of type Connected_Data or Connected_ID.*/
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.
@pre Destination must be a valid instance.*/
NodePredicateBase::ConstPointer GetSourcesDetector(const IPropertyOwner *destination, RelationType minimalRelation = RelationType::Implicit_Data) 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.
@pre Destination must be a valid instance.*/
NodePredicateBase::ConstPointer GetDestinationsDetector(const IPropertyOwner *source, RelationType minimalRelation = RelationType::Implicit_Data) const;
/**Returns a predicate that can be used to find the Destination of the passed Source for a given relationID.
@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 relationID must identify a relation of the passed source and rule. (This must be in the return of this->GetExistingRelations(source). */
NodePredicateBase::ConstPointer GetDestinationDetector(const IPropertyOwner *source, RelationIDType relationID) 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).
@pre source must be a valid instance.
@pre destination must be a valid instance.*/
void Connect(IPropertyOwner *source, const IPropertyOwner *destination) const;
/**Disconnect the passed instances. Afterwards they have a relation of None or Implicit_Data.
All relationID-properties 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 IPropertyOwner *destination) const;
/**Disconnect the source from the passed relationID (usefull for "zombie relations").
All relationID-properties in the source for the passed relationID will be removed.
If the relationID is not part of the source. Nothing will be changed.
@pre source must be a valid instance.*/
void Disconnect(IPropertyOwner *source, RelationIDType relationID) const;
protected:
/** Is called by HasRelation() if no relation is evident in the ID-layer.
Implement this method to deduce if the passed instances have a relation of type
None, Implicit_Data or Connected_Data.
@pre source must be a pointer to a valid IPropertyOwner instance.
@pre destination must be a pointer to a valid IPropertyOwner instance.
*/
virtual RelationType HasRelation_datalayer(const IPropertyOwner *source, const IPropertyOwner *destination) const =0;
/** Is called by GetRelationID() if 1) the relation ID cannot be deduced on the ID-layer
and 2) there are relationID-properties that indicated at least a connected relation.
Implement this method to check which existing relations as Connected_Data exists between
both passed instances. If the passed instances have no
explicit relation (so of type Connected_Data) no ID can be deduced an exception
will be thrown.
@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 a relation of type Connected_Data or Connected_ID*/
virtual RelationIDType GetRelationID_datalayer(const IPropertyOwner *source, const IPropertyOwner *destination) const;
/**Is called by Connect() to ensure that source has correctly set properties to resample
the relation. This means that the method should set the properties that describe and encode the relation (relation-properties),
as well as the relationID-properties that are needed to identify which properties are relation-properties of this relations.
If the passed instance are already connected the old connection will be overwritten (and raised to the highest possible connection level)
Connect() will ensure that source and destination are valid pointers.
@param relationID is the ID for the relation that should be connected. It is queried by Connect() via GetRelationID() or created if needed.
@pre source must be a valid instance.
@pre destination must be a valid instance.*/
virtual void Connect_datalayer(IPropertyOwner *source, const IPropertyOwner *destination, RelationIDType relationID) const = 0;
/**This method is called by Disconnect() to remove all properties of the relation from the source.
@remark All relationID-properties of this relation will removed by Disconnect() after this method call.
If the relationID is not part of the source. Nothing will be changed. Disconnect() ensures that source is a valid pointer if called.
@pre source must be a valid instance.*/
virtual void Disconnect_datalayer(IPropertyOwner *source, RelationIDType relationID) const = 0;
};
Remarks to implementation details:
# I would propose to use the same UID technique for RelationIDs that is used in IIdentifiable.
# Would be sensible to use the PropertyKeyPath class for handling the relationID-properties.