diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 6127764eb7..c7f2f109ba 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,321 +1,323 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkCompositePixelValueToString.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkExtractSliceFilter2.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp + DataManagement/mitkGenericIDRelationRule.cpp DataManagement/mitkIdentifiable.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkIPropertyProvider.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDataUID.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkNodePredicateUID.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyKeyPath.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp + DataManagement/mitkPropertyRelationRuleBase.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkDicomSeriesReader.cpp IO/mitkDicomSeriesReaderService.cpp IO/mitkDicomSR_GantryTiltInformation.cpp IO/mitkDicomSR_ImageBlockDescriptor.cpp IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp IO/mitkDicomSR_LoadDICOMRGBPixel.cpp IO/mitkDicomSR_LoadDICOMScalar4D.cpp IO/mitkDicomSR_LoadDICOMScalar.cpp IO/mitkDicomSR_SliceGroupingResult.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/DisplayConfigMITKNoCrosshair.xml Interactions/DisplayConfigMITKRotation.xml Interactions/DisplayConfigMITKRotationUnCoupled.xml Interactions/DisplayConfigMITKSwivel.xml Interactions/DisplayConfigMITKLimited.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/mitkBaseData.h b/Modules/Core/include/mitkBaseData.h index 0e899f83dc..62d6853138 100644 --- a/Modules/Core/include/mitkBaseData.h +++ b/Modules/Core/include/mitkBaseData.h @@ -1,420 +1,421 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #define BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #include #include "mitkBaseProcess.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #include "mitkOperationActor.h" #include "mitkPropertyList.h" #include "mitkTimeGeometry.h" #include namespace mitk { // class BaseProcess; //##Documentation //## @brief Base of all data objects //## //## Base of all data objects, e.g., images, contours, surfaces etc. Inherits //## from itk::DataObject and thus can be included in a pipeline. //## Inherits also from OperationActor and can be used as a destination for Undo //## @ingroup Data class MITKCORE_EXPORT BaseData : public itk::DataObject, public OperationActor, public Identifiable, public IPropertyOwner { public: mitkClassMacroItkParent(BaseData, itk::DataObject) // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; + virtual void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ const mitk::TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry.GetPointer(); } /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. * \deprecatedSince{2013_09} Please use GetTimeGeometry instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(const mitk::TimeGeometry *GetTimeSlicedGeometry() const) { return GetTimeGeometry(); } /** * @brief Return the TimeGeometry of the data as pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ mitk::TimeGeometry *GetTimeGeometry() { return m_TimeGeometry.GetPointer(); } /** * @brief Return the TimeGeometry of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). */ const mitk::TimeGeometry *GetUpdatedTimeGeometry(); /** * @brief Return the TimeGeometry of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). * \deprecatedSince{2013_09} Please use GetUpdatedTimeGeometry instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(const mitk::TimeGeometry *GetUpdatedTimeSliceGeometry()) { return GetUpdatedTimeGeometry(); } /** * \brief Expands the TimeGeometry to a number of TimeSteps. * * The method expands the TimeGeometry to the given number of TimeSteps, * filling newly created elements with empty geometries. Sub-classes should override * this method to handle the elongation of their data vectors, too. * Note that a shrinking is neither possible nor intended. */ virtual void Expand(unsigned int timeSteps); /** * \brief Return the BaseGeometry of the data at time \a t. * * The method does not simply return * m_TimeGeometry->GetGeometry(t). * Before doing this, it makes sure that the BaseGeometry is up-to-date * (by setting the update extent appropriately and calling * UpdateOutputInformation). * * @todo Appropriate setting of the update extent is missing. */ const mitk::BaseGeometry *GetUpdatedGeometry(int t = 0); //##Documentation //## @brief Return the geometry, which is a TimeGeometry, of the data //## as non-const pointer. //## //## \warning No update will be called. Use GetUpdatedGeometry() if you cannot //## be sure that the geometry is up-to-date. //## //## Normally used in GenerateOutputInformation of subclasses of BaseProcess. mitk::BaseGeometry *GetGeometry(int t = 0) const { if (m_TimeGeometry.IsNull()) return nullptr; return m_TimeGeometry->GetGeometryForTimeStep(t); } //##Documentation //## @brief Update the information for this BaseData (the geometry in particular) //## so that it can be used as an output of a BaseProcess. //## //## This method is used in the pipeline mechanism to propagate information and //## initialize the meta data associated with a BaseData. Any implementation //## of this method in a derived class is assumed to call its source's //## BaseProcess::UpdateOutputInformation() which determines modified //## times, LargestPossibleRegions, and any extra meta data like spacing, //## origin, etc. Default implementation simply call's it's source's //## UpdateOutputInformation(). //## \note Implementations of this methods in derived classes must take care //## that the geometry is updated by calling //## GetTimeGeometry()->UpdateInformation() //## \em after calling its source's BaseProcess::UpdateOutputInformation(). void UpdateOutputInformation() override; //##Documentation //## @brief Set the RequestedRegion to the LargestPossibleRegion. //## //## This forces a filter to produce all of the output in one execution //## (i.e. not streaming) on the next call to Update(). void SetRequestedRegionToLargestPossibleRegion() override = 0; //##Documentation //## @brief Determine whether the RequestedRegion is outside of the BufferedRegion. //## //## This method returns true if the RequestedRegion //## is outside the BufferedRegion (true if at least one pixel is //## outside). This is used by the pipeline mechanism to determine //## whether a filter needs to re-execute in order to satisfy the //## current request. If the current RequestedRegion is already //## inside the BufferedRegion from the previous execution (and the //## current filter is up to date), then a given filter does not need //## to re-execute bool RequestedRegionIsOutsideOfTheBufferedRegion() override = 0; //##Documentation //## @brief Verify that the RequestedRegion is within the LargestPossibleRegion. //## //## If the RequestedRegion is not within the LargestPossibleRegion, //## then the filter cannot possibly satisfy the request. This method //## returns true if the request can be satisfied (even if it will be //## necessary to process the entire LargestPossibleRegion) and //## returns false otherwise. This method is used by //## PropagateRequestedRegion(). PropagateRequestedRegion() throws a //## InvalidRequestedRegionError exception if the requested region is //## not within the LargestPossibleRegion. bool VerifyRequestedRegion() override = 0; //##Documentation //## @brief Copy information from the specified data set. //## //## This method is part of the pipeline execution model. By default, a //## BaseProcess will copy meta-data from the first input to all of its //## outputs. See ProcessObject::GenerateOutputInformation(). Each //## subclass of DataObject is responsible for being able to copy //## whatever meta-data it needs from another DataObject. //## The default implementation of this method copies the time sliced geometry //## and the property list of an object. If a subclass overrides this //## method, it should always call its superclass' version. void CopyInformation(const itk::DataObject *data) override; //##Documentation //## @brief Check whether the data has been initialized, i.e., //## at least the Geometry and other header data has been set //## //## \warning Set to \a true by default for compatibility reasons. //## Set m_Initialized=false in constructors of sub-classes that //## support distinction between initialized and uninitialized state. virtual bool IsInitialized() const; //##Documentation //## @brief Calls ClearData() and InitializeEmpty(); //## \warning Only use in subclasses that reimplemented these methods. //## Just calling Clear from BaseData will reset an object to a not initialized, //## invalid state. virtual void Clear(); //##Documentation //## @brief Check whether object contains data (at //## a specified time), e.g., a set of points may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmptyTimeStep(unsigned int t) const; //##Documentation //## @brief Check whether object contains data (at //## least at one point in time), e.g., a set of points //## may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmpty() const; //##Documentation //## @brief Set the requested region from this data object to match the requested //## region of the data object passed in as a parameter. //## //## This method is implemented in the concrete subclasses of BaseData. void SetRequestedRegion(const itk::DataObject *data) override = 0; //##Documentation //##@brief overwrite if the Data can be called by an Interactor (StateMachine). //## //## Empty by default. Overwrite and implement all the necessary operations here //## and get the necessary information from the parameter operation. void ExecuteOperation(Operation *operation) override; /** * \brief Set the BaseGeometry of the data, which will be referenced (not copied!). * Assumes the data object has only 1 time step ( is a 3D object ) and creates a * new TimeGeometry which saves the given BaseGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedGeometry */ virtual void SetGeometry(BaseGeometry *aGeometry3D); /** * \brief Set the TimeGeometry of the data, which will be referenced (not copied!). * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedTimeGeometry */ virtual void SetTimeGeometry(TimeGeometry *geometry); /** * \brief Set a clone of the provided Geometry as Geometry of the data. * Assumes the data object has only 1 time step ( is a 3D object ) and * creates a new TimeGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * \sa SetGeometry */ virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D); /** * \brief Set a clone of the provided TimeGeometry as TimeGeometry of the data. * * \sa SetGeometry */ virtual void SetClonedTimeGeometry(const TimeGeometry *geometry); //##Documentation //## @brief Set a clone of the provided geometry as BaseGeometry of a given time step. //## //## \sa SetGeometry virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time); //##Documentation //## @brief Get the data's property list //## @sa GetProperty //## @sa m_PropertyList mitk::PropertyList::Pointer GetPropertyList() const; //##Documentation //## @brief Set the data's property list //## @sa SetProperty //## @sa m_PropertyList void SetPropertyList(PropertyList *propertyList); //##Documentation //## @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList, //## and set it to this, respectively; //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists mitk::BaseProperty::Pointer GetProperty(const char *propertyKey) const; void SetProperty(const char *propertyKey, BaseProperty *property); //##Documentation //## @brief Convenience method for setting the origin of //## the BaseGeometry instances of all time steps //## //## \warning Geometries contained in the BaseGeometry will //## \em not be changed, e.g. in case the BaseGeometry is a //## SlicedGeometry3D the origin will \em not be propagated //## to the contained slices. The sub-class SlicedData //## does this for the case that the SlicedGeometry3D is //## evenly spaced. virtual void SetOrigin(const Point3D &origin); /** \brief Get the process object that generated this data object. * * If there is no process object, then the data object has * been disconnected from the pipeline, or the data object * was created manually. (Note: we cannot use the GetObjectMacro() * defined in itkMacro because the mutual dependency of * DataObject and ProcessObject causes compile problems. Also, * a forward reference smart pointer is returned, not a smart pointer, * because of the circular dependency between the process and data object.) * * GetSource() returns a SmartPointer and not a WeakPointer * because it is assumed the code calling GetSource() wants to hold a * long term reference to the source. */ itk::SmartPointer GetSource() const; //##Documentation //## @brief Get the number of time steps from the TimeGeometry //## As the base data has not a data vector given by itself, the number //## of time steps is defined over the time sliced geometry. In sub classes, //## a better implementation could be over the length of the data vector. unsigned int GetTimeSteps() const { return m_TimeGeometry->CountTimeSteps(); } //##Documentation //## @brief Get the modified time of the last change of the contents //## this data object or its geometry. unsigned long GetMTime() const override; /** * \sa itk::ProcessObject::Graft */ void Graft(const DataObject *) override; protected: BaseData(); BaseData(const BaseData &other); ~BaseData() override; //##Documentation //## \brief Initialize the TimeGeometry for a number of time steps. //## The TimeGeometry is initialized empty and evenly timed. //## In many cases it will be necessary to overwrite this in sub-classes. virtual void InitializeTimeGeometry(unsigned int timeSteps = 1); /** * \brief Initialize the TimeGeometry for a number of time steps. * The TimeGeometry is initialized empty and evenly timed. * In many cases it will be necessary to overwrite this in sub-classes. * \deprecatedSince{2013_09} Please use GetUpdatedTimeGeometry instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(virtual void InitializeTimeSlicedGeometry(unsigned int timeSteps = 1)) { InitializeTimeGeometry(timeSteps); } //##Documentation //## @brief reset to non-initialized state, release memory virtual void ClearData(); //##Documentation //## @brief Pure virtual; Must be used in subclasses to get a data object to a //## valid state. Should at least create one empty object and call //## Superclass::InitializeTimeGeometry() to ensure an existing valid geometry virtual void InitializeEmpty() {} void PrintSelf(std::ostream &os, itk::Indent indent) const override; bool m_LastRequestedRegionWasOutsideOfTheBufferedRegion; mutable unsigned int m_SourceOutputIndexDuplicate; bool m_Initialized; private: //##Documentation //## @brief PropertyList, f.e. to hold pic-tags, tracking-data,.. //## PropertyList::Pointer m_PropertyList; TimeGeometry::Pointer m_TimeGeometry; }; } // namespace mitk #endif /* BASEDATA_H_HEADER_INCLUDED_C1EBB6FA */ diff --git a/Modules/Core/include/mitkDataNode.h b/Modules/Core/include/mitkDataNode.h index 24575ee655..43345060a6 100644 --- a/Modules/Core/include/mitkDataNode.h +++ b/Modules/Core/include/mitkDataNode.h @@ -1,611 +1,612 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef DATATREENODE_H_HEADER_INCLUDED_C1E14338 #define DATATREENODE_H_HEADER_INCLUDED_C1E14338 #include "mitkBaseData.h" //#include "mitkMapper.h" #include "mitkDataInteractor.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #ifdef MBI_NO_STD_NAMESPACE #define MBI_STD #include #include #else #define MBI_STD std #include #include #endif #include "mitkColorProperty.h" #include "mitkPropertyList.h" #include "mitkStringProperty.h" //#include "mitkMapper.h" #include "mitkGeometry3D.h" #include "mitkLevelWindow.h" #include #include class vtkLinearTransform; namespace mitk { class BaseRenderer; class Mapper; /** * \brief Class for nodes of the DataTree * * Contains the data (instance of BaseData), a list of mappers, which can * draw the data, a transform (vtkTransform) and a list of properties * (PropertyList). * \ingroup DataManagement * * \todo clean up all the GetProperty methods. There are too many different flavours... Can most probably be reduced * to * bool GetProperty(type&) * * \warning Change in semantics of SetProperty() since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). */ class MITKCORE_EXPORT DataNode : public itk::DataObject, public Identifiable, public IPropertyOwner { public: typedef mitk::Geometry3D::Pointer Geometry3DPointer; typedef std::vector> MapperVector; typedef std::map MapOfPropertyLists; typedef std::vector PropertyListKeyNames; typedef std::set GroupTagList; /** * \brief Definition of an itk::Event that is invoked when * a DataInteractor is set on this DataNode. */ itkEventMacro(InteractorChangedEvent, itk::AnyEvent) mitkClassMacroItkParent(DataNode, itk::DataObject) itkFactorylessNewMacro(Self) itkCloneMacro(Self) // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; + virtual void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; mitk::Mapper *GetMapper(MapperSlotId id) const; /** * \brief Get the data object (instance of BaseData, e.g., an Image) * managed by this DataNode */ BaseData *GetData() const; /** * \brief Get the transformation applied prior to displaying the data as * a vtkTransform * \deprecated use GetData()->GetGeometry()->GetVtkTransform() instead */ vtkLinearTransform *GetVtkTransform(int t = 0) const; /** * \brief Set the data object (instance of BaseData, e.g., an Image) * managed by this DataNode * * Prior set properties are kept if previous data of the node already exists and has the same * type as the new data to be set. Otherwise, the default properties are used. * In case that previous data already exists, the property list of the data node is cleared * before setting new default properties. * * \warning the actor-mode of the vtkInteractor does not work any more, if the transform of the * data-tree-node is connected to the transform of the basedata via vtkTransform->SetInput. */ virtual void SetData(mitk::BaseData *baseData); /** * \brief Set the Interactor. */ virtual void SetDataInteractor(const DataInteractor::Pointer interactor); virtual DataInteractor::Pointer GetDataInteractor() const; mitk::DataNode &operator=(const DataNode &right); mitk::DataNode &operator=(BaseData *right); virtual void SetMapper(MapperSlotId id, mitk::Mapper *mapper); void UpdateOutputInformation() override; void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; void SetRequestedRegion(const itk::DataObject *data) override; void CopyInformation(const itk::DataObject *data) override; /** * \brief The "names" used for (renderer-specific) PropertyLists in GetPropertyList(string). * * All possible values for the "renderer" parameters of * the diverse GetProperty/List() methods. */ PropertyListKeyNames GetPropertyListNames() const; /** * \brief Set the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-value. * * \warning Change in semantics since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). * * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void SetProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Replace the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-reference. * * If \a renderer is \a nullptr the property is set in the BaseRenderer-independent * PropertyList of this DataNode. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void ReplaceProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Add the property (instance of BaseProperty) if it does * not exist (or always if\a overwrite is\a true) * with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent * PropertyList). This is set-by-value. * * For\a overwrite ==\a false the property is\em not changed * if it already exists. For\a overwrite ==\a true the method * is identical to SetProperty. * * \sa SetProperty * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void AddProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** * \brief Get the PropertyList of the \a renderer. If \a renderer is \a * nullptr, the BaseRenderer-independent PropertyList of this DataNode * is returned. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::PropertyList *GetPropertyList(const mitk::BaseRenderer *renderer = nullptr) const; mitk::PropertyList *GetPropertyList(const std::string &rendererName) const; /** * \brief Add values from another PropertyList. * * Overwrites values in m_PropertyList only when possible (i.e. when types are compatible). * If you want to allow for object type changes (replacing a "visible":BoolProperty with "visible":IntProperty, * set the \param replace. * * \param replace true: if \param pList contains a property "visible" of type ColorProperty and our m_PropertyList * also has a "visible" property of a different type (e.g. BoolProperty), change the type, i.e. replace the objects * behind the pointer. * * \sa SetProperty * \sa ReplaceProperty * \sa m_PropertyList */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); /** * \brief Get the property (instance of BaseProperty) with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * * If \a fallBackOnDataProperties is true, the data property list is queried as a last resort. * * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::BaseProperty *GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr, bool fallBackOnDataProperties = true) const; /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(itk::SmartPointer &property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property.IsNotNull(); } /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(T *&property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property != nullptr; } /** * \brief Convenience access method for GenericProperty properties * (T being the type of the second parameter) * \return \a true property was found */ template bool GetPropertyValue(const char *propertyKey, T &value, const mitk::BaseRenderer *renderer = nullptr) const { GenericProperty *gp = dynamic_cast *>(GetProperty(propertyKey, renderer)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /// \brief Get a set of all group tags from this node's property list GroupTagList GetGroupTags() const; /** * \brief Convenience access method for bool properties (instances of * BoolProperty) * \return \a true property was found */ bool GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for int properties (instances of * IntProperty) * \return \a true property was found */ bool GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for float properties (instances of * FloatProperty) * \return \a true property was found */ bool GetFloatProperty(const char *propertyKey, float &floatValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for double properties (instances of * DoubleProperty) * * If there is no DoubleProperty for the given\c propertyKey argument, the method * looks for a corresponding FloatProperty instance. * * \return \a true property was found */ bool GetDoubleProperty(const char *propertyKey, double &doubleValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for string properties (instances of * StringProperty) * \return \a true property was found */ bool GetStringProperty(const char *propertyKey, std::string &string, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for color properties (instances of * ColorProperty) * \return \a true property was found */ bool GetColor(float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color") const; /** * \brief Convenience access method for level-window properties (instances of * LevelWindowProperty) * \return \a true property was found */ bool GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow") const; /** * \brief set the node as selected */ void SetSelected(bool selected, const mitk::BaseRenderer *renderer = nullptr); /** * \brief set the node as selected * \return \a true node is selected */ bool IsSelected(const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name") * \return \a true property was found */ bool GetName(std::string &nodeName, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "name") const { return GetStringProperty(propertyKey, nodeName, renderer); } /** * \brief Extra convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name"). * * This method does not take the renderer specific * propertylists into account, because the name of an object should never be renderer specific. * \returns a std::string with the name of the object (content of "name" Property). * If there is no "name" Property, an empty string will be returned. */ virtual std::string GetName() const { mitk::StringProperty *sp = dynamic_cast(this->GetProperty("name")); if (sp == nullptr) return ""; return sp->GetValue(); } /** * \brief Extra convenience access method to set the name of an object. * * The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const char *name) { if (name == nullptr) return; this->SetProperty("name", StringProperty::New(name)); } /** * \brief Extra convenience access method to set the name of an object. * * The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const std::string name) { this->SetName(name.c_str()); } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty with property-key "visible") * \return \a true property was found * \sa IsVisible */ bool GetVisibility(bool &visible, const mitk::BaseRenderer *renderer, const char *propertyKey = "visible") const { return GetBoolProperty(propertyKey, visible, renderer); } /** * \brief Convenience access method for opacity properties (instances of * FloatProperty) * \return \a true property was found */ bool GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey = "opacity") const; /** * \brief Convenience access method for boolean properties (instances * of BoolProperty). Return value is the value of the property. If the property is * not found, the value of \a defaultIsOn is returned. * * Thus, the return value has a different meaning than in the * GetBoolProperty method! * \sa GetBoolProperty */ bool IsOn(const char *propertyKey, const mitk::BaseRenderer *renderer, bool defaultIsOn = true) const { if (propertyKey == nullptr) return defaultIsOn; GetBoolProperty(propertyKey, defaultIsOn, renderer); return defaultIsOn; } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty). Return value is the visibility. Default is * visible==true, i.e., true is returned even if the property (\a * propertyKey) is not found. * * Thus, the return value has a different meaning than in the * GetVisibility method! * \sa GetVisibility * \sa IsOn */ bool IsVisible(const mitk::BaseRenderer *renderer, const char *propertyKey = "visible", bool defaultIsOn = true) const { return IsOn(propertyKey, renderer, defaultIsOn); } /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(float red, float green, float blue, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting visibility properties (instances * of BoolProperty) * \param visible If set to true, the data will be rendered. If false, the render will skip this data. * \param renderer Specify a renderer if the visibility shall be specific to a renderer * \param propertykey Can be used to specify a user defined name of the visibility propery. */ void SetVisibility(bool visible, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "visible"); /** * \brief Convenience method for setting opacity properties (instances of * FloatProperty) */ void SetOpacity(float opacity, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "opacity"); /** * \brief Convenience method for setting level-window properties * (instances of LevelWindowProperty) */ void SetLevelWindow(mitk::LevelWindow levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow"); /** * \brief Convenience method for setting int properties (instances of * IntProperty) */ void SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting boolean properties (instances of * BoolProperty) */ void SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting float properties (instances of * FloatProperty) */ void SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting double properties (instances of * DoubleProperty) */ void SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting string properties (instances of * StringProperty) */ void SetStringProperty(const char *propertyKey, const char *string, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Get the timestamp of the last change of the contents of this node or * the referenced BaseData. */ unsigned long GetMTime() const override; /** * \brief Get the timestamp of the last change of the reference to the * BaseData. */ unsigned long GetDataReferenceChangedTime() const { return m_DataReferenceChangedTime.GetMTime(); } protected: DataNode(); ~DataNode() override; /// Invoked when the property list was modified. Calls Modified() of the DataNode virtual void PropertyListModified(const itk::Object *caller, const itk::EventObject &event); /// \brief Mapper-slots mutable MapperVector m_Mappers; /** * \brief The data object (instance of BaseData, e.g., an Image) managed * by this DataNode */ BaseData::Pointer m_Data; /** * \brief BaseRenderer-independent PropertyList * * Properties herein can be overwritten specifically for each BaseRenderer * by the BaseRenderer-specific properties defined in m_MapOfPropertyLists. */ PropertyList::Pointer m_PropertyList; /// \brief Map associating each BaseRenderer with its own PropertyList mutable MapOfPropertyLists m_MapOfPropertyLists; DataInteractor::Pointer m_DataInteractor; /// \brief Timestamp of the last change of m_Data itk::TimeStamp m_DataReferenceChangedTime; unsigned long m_PropertyListModifiedObserverTag; }; #if (_MSC_VER > 1200) || !defined(_MSC_VER) MITKCORE_EXPORT MBI_STD::istream &operator>>(MBI_STD::istream &i, DataNode::Pointer &dtn); MITKCORE_EXPORT MBI_STD::ostream &operator<<(MBI_STD::ostream &o, DataNode::Pointer &dtn); #endif } // namespace mitk #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MITKCORE_EXPORT MBI_STD::istream &operator>>(MBI_STD::istream &i, mitk::DataNode::Pointer &dtn); MITKCORE_EXPORT MBI_STD::ostream &operator<<(MBI_STD::ostream &o, mitk::DataNode::Pointer &dtn); #endif #endif /* DATATREENODE_H_HEADER_INCLUDED_C1E14338 */ diff --git a/Modules/Core/include/mitkGenericIDRelationRule.h b/Modules/Core/include/mitkGenericIDRelationRule.h new file mode 100644 index 0000000000..5dd82d44dc --- /dev/null +++ b/Modules/Core/include/mitkGenericIDRelationRule.h @@ -0,0 +1,138 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkGenericIDRelationRule_h +#define mitkGenericIDRelationRule_h + +#include "mitkPropertyRelationRuleBase.h" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4251) +#endif + +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 */ + virtual RuleIDType GetRuleID() const override; + + /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ + virtual 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.*/ + virtual 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.*/ + virtual std::string GetDestinationRoleName() const override; + + 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); + virtual ~GenericIDRelationRule() = 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.*/ + virtual 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. + */ + virtual 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.*/ + virtual 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.*/ + virtual void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const override; + + virtual itk::LightObject::Pointer InternalClone() const override; + + private: + RuleIDType m_RuleIDTag; + std::string m_DisplayName; + std::string m_SourceRole; + std::string m_DestinationRole; + }; + +} // namespace mitk + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif diff --git a/Modules/Core/include/mitkIPropertyOwner.h b/Modules/Core/include/mitkIPropertyOwner.h index b4b2d01227..35248bf728 100644 --- a/Modules/Core/include/mitkIPropertyOwner.h +++ b/Modules/Core/include/mitkIPropertyOwner.h @@ -1,59 +1,85 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkIPropertyOwner_h #define mitkIPropertyOwner_h -#include #include +#include namespace mitk { + /** Interface implemented by classes that provide write/read access to property information.*/ class MITKCORE_EXPORT IPropertyOwner : public IPropertyProvider { public: ~IPropertyOwner() override; /** - * \brief Get property by its key. - * - * \param[in] propertyKey Key of property. - * \param[in] contextName Optional, default is empty string (default - * context). Search in specified context. - * \param[in] fallBackOnDefaultContext Optional, default is true. Also - * search in default context if property was not found in given - * context. - * - * \return Found property, nullptr otherwise. - */ - virtual BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) = 0; + * \brief Get property by its key. + * + * \param[in] propertyKey Key of property. + * \param[in] contextName Optional, default is empty string (default + * context). Search in specified context. + * \param[in] fallBackOnDefaultContext Optional, default is true. Also + * search in default context if property was not found in given + * context. + * + * \return Found property, nullptr otherwise. + */ + virtual BaseProperty *GetNonConstProperty(const std::string &propertyKey, + const std::string &contextName = "", + bool fallBackOnDefaultContext = true) = 0; /** * \brief Add new or change existent property. * * \param[in] propertyKey Key of property. * \param[in] property The actual property. * \param[in] contextName Optional, default is empty string (default context). Context in which the property is set. * \param[in] fallBackOnDefaultContext Optional, default is false. Set * property in default context if given context does not * exist. + * \pre If a context is specified by contextName, this context must be known by the owner or fallBackOnDefaultContext + must be true. If violated an mitk::Exception is thrown. + * \pre propertyKey must be a none empty string. If violated an mitk::Exception is thrown. + */ + virtual void SetProperty(const std::string &propertyKey, + BaseProperty *property, + const std::string &contextName = "", + bool fallBackOnDefaultContext = false) = 0; + + /** + * \brief Removes a property. If the property does not exist, nothing will be done. + * + * \param[in] propertyKey Key of property. + * \param[in] contextName Optional, default is empty string (default + context). Context in which the property is removed. + * \param[in] fallBackOnDefaultContext Optional, default is false. Remove + * property in default context if given context does not + * exist. + * \pre If a context is specified by contextName, this context must be known by the owner or fallBackOnDefaultContext + must be true. If violated an mitk::Exception is thrown. + * \pre propertyKey must be a none empty string. If violated an mitk::Exception is thrown. */ - virtual void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) = 0; + virtual void RemoveProperty(const std::string &propertyKey, + const std::string &contextName = "", + bool fallBackOnDefaultContext = false) = 0; }; -} +} // namespace mitk #endif diff --git a/Modules/Core/include/mitkIPropertyProvider.h b/Modules/Core/include/mitkIPropertyProvider.h index 440516d80b..d82a6104ee 100644 --- a/Modules/Core/include/mitkIPropertyProvider.h +++ b/Modules/Core/include/mitkIPropertyProvider.h @@ -1,65 +1,71 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkIPropertyProvider_h #define mitkIPropertyProvider_h -#include #include +#include namespace mitk { + /** Interface implemented by classes that provide access to property information. + @remark: This classes may not be the owner of the property, thus only grants const access to it. + See IPropertyOwner for ownership.*/ class MITKCORE_EXPORT IPropertyProvider { public: virtual ~IPropertyProvider(); /** - * \brief Get property by its key. - * - * \param[in] propertyKey Key of property. - * \param[in] contextName Optional, default is empty string (default - * context). Search in specified context. - * \param[in] fallBackOnDefaultContext Optional, default is true. Also - * search in default context if property was not found in given - * context. - * - * \return Found property, nullptr otherwise. - */ - virtual BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const = 0; + * \brief Get property by its key. + * + * \param[in] propertyKey Key of property. + * \param[in] contextName Optional, default is empty string (default + * context). Search in specified context. + * \param[in] fallBackOnDefaultContext Optional, default is true. Also + * search in default context if property was not found in given + * context. + * + * \return Found property, nullptr otherwise. + */ + virtual BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, + const std::string &contextName = "", + bool fallBackOnDefaultContext = true) const = 0; /** - * \brief Query keys of existing properties. - * - * \param[in] contextName Optional, default is empty string (default - * context). Search in specified context. - * \param[in] includeDefaultContext Optional, default is false. Include - * default context. - * - * \return List of property keys. - */ - virtual std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const = 0; + * \brief Query keys of existing properties. + * + * \param[in] contextName Optional, default is empty string (default + * context). Search in specified context. + * \param[in] includeDefaultContext Optional, default is false. Include + * default context. + * + * \return List of property keys. + */ + virtual std::vector GetPropertyKeys(const std::string &contextName = "", + bool includeDefaultContext = false) const = 0; /** - * \brief Query names of existing contexts. - * - * \return List of context names. - */ + * \brief Query names of existing contexts. + * + * \return List of context names. + */ virtual std::vector GetPropertyContextNames() const = 0; }; -} +} // namespace mitk #endif diff --git a/Modules/Core/include/mitkPropertyRelationRuleBase.h b/Modules/Core/include/mitkPropertyRelationRuleBase.h new file mode 100644 index 0000000000..62846d7da5 --- /dev/null +++ b/Modules/Core/include/mitkPropertyRelationRuleBase.h @@ -0,0 +1,296 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkPropertyRelationRuleBase_h +#define mitkPropertyRelationRuleBase_h + +#include "mitkIPropertyOwner.h" +#include "mitkIdentifiable.h" + +#include "mitkException.h" +#include "mitkNodePredicateBase.h" +#include "mitkPropertyKeyPath.h" + +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4251) +#endif + +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). + - A concrete rule ID 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 purpuse. One may implement an interim or joined class that manages the mutual + stuff, but the registered instances must be one for "DICOM source input image" and one for + "DICOM source mask" and 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. + - 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. + 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|]" + - : The identifier of the concrete rule that sets the property + - : The unique index of the relation of the rule 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. + - : 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 + "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 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; + + /** 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. + @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. + @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; + + /** 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). + @pre source must be a pointer to a valid IPropertyOwner instance. + */ + RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source) 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. + @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; otherwise + NoPropertyRelationException 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. + 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. + @pre Destination must be a valid instance.*/ + NodePredicateBase::ConstPointer GetSourcesDetector( + const IPropertyProvider *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 IPropertyProvider *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 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; + + /**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 IPropertyProvider *destination) 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. + @pre source must be a valid instance. + @pre destination must be a valid instance.*/ + void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination) 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. + 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; + + protected: + PropertyRelationRuleBase() = default; + virtual ~PropertyRelationRuleBase() = default; + + using InstanceIDType = std::string; + using InstanceIDVectorType = std::vector; + static InstanceIDType NULL_INSTANCE_ID(); + + /** Returns the instance ID 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), + NULL_INSTANCE_ID will be returned to imply the non existing relation. + @pre source must be a pointer to a valid IPropertyProvider instance. + @pre destination must be a pointer to a valid IPropertyProvider instance.*/ + InstanceIDType 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 rule. But 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 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.*/ + 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; + + /**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; + + /**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 + 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; + + /** Returns the generic root path for relation rules ("MITK.Relations").*/ + static PropertyKeyPath GetRootKeyPath(); + + /** Returns the root path for the rule instance ("MITK.Relations."). */ + PropertyKeyPath GetRuleRootKeyPath() const; + + virtual itk::LightObject::Pointer InternalClone() const override; + + private: + /** Creats a relation UID*/ + static RelationUIDType CreateRelationUID(); + + /**Prepares a new relation instance. Therefore an unused and valid instance ID for the passed source and the rule + instance 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 + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif diff --git a/Modules/Core/src/DataManagement/mitkBaseData.cpp b/Modules/Core/src/DataManagement/mitkBaseData.cpp index a6f5bad336..1bb6171a7c 100644 --- a/Modules/Core/src/DataManagement/mitkBaseData.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseData.cpp @@ -1,347 +1,361 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBaseData.h" #include #include #include #include #include mitk::BaseData::BaseData() : m_SourceOutputIndexDuplicate(0), m_Initialized(true), m_PropertyList(PropertyList::New()), m_TimeGeometry(ProportionalTimeGeometry::New()) { } mitk::BaseData::BaseData(const BaseData &other) : itk::DataObject(), OperationActor(), Identifiable(), m_SourceOutputIndexDuplicate(other.m_SourceOutputIndexDuplicate), m_Initialized(other.m_Initialized), m_PropertyList(other.m_PropertyList->Clone()), m_TimeGeometry(other.m_TimeGeometry->Clone()) { } mitk::BaseData::~BaseData() { } void mitk::BaseData::InitializeTimeGeometry(unsigned int timeSteps) { mitk::Geometry3D::Pointer geo3D = mitk::Geometry3D::New(); mitk::BaseGeometry::Pointer baseGeo = dynamic_cast(geo3D.GetPointer()); baseGeo->Initialize(); // The geometry is propagated automatically to the other items, // if EvenlyTimed is true... // Old timeGeometry->InitializeEvenlyTimed( g3d.GetPointer(), timeSteps ); TimeGeometry::Pointer timeGeometry = this->GetTimeGeometry(); timeGeometry->Initialize(); timeGeometry->Expand(timeSteps); for (TimeStepType step = 0; step < timeSteps; ++step) { timeGeometry->SetTimeStepGeometry(baseGeo.GetPointer(), step); } } void mitk::BaseData::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->UpdateBoundingBox(); } } const mitk::TimeGeometry *mitk::BaseData::GetUpdatedTimeGeometry() { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetTimeGeometry(); } void mitk::BaseData::Expand(unsigned int timeSteps) { if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->Expand(timeSteps); } else { this->InitializeTimeGeometry(timeSteps); } } const mitk::BaseGeometry *mitk::BaseData::GetUpdatedGeometry(int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetGeometry(t); } void mitk::BaseData::SetGeometry(BaseGeometry *geometry) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); if (geometry != nullptr) { timeGeometry->Initialize(geometry, 1); } SetTimeGeometry(timeGeometry); return; } void mitk::BaseData::SetTimeGeometry(TimeGeometry *geometry) { m_TimeGeometry = geometry; this->Modified(); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D) { SetGeometry(static_cast(aGeometry3D->Clone().GetPointer())); } void mitk::BaseData::SetClonedTimeGeometry(const TimeGeometry *geometry) { TimeGeometry::Pointer clonedGeometry = geometry->Clone(); SetTimeGeometry(clonedGeometry.GetPointer()); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time) { if (m_TimeGeometry) { m_TimeGeometry->SetTimeStepGeometry(static_cast(aGeometry3D->Clone().GetPointer()), time); } } bool mitk::BaseData::IsEmptyTimeStep(unsigned int) const { return IsInitialized() == false; } bool mitk::BaseData::IsEmpty() const { if (IsInitialized() == false) return true; const TimeGeometry *timeGeometry = const_cast(this)->GetUpdatedTimeGeometry(); if (timeGeometry == nullptr) return true; unsigned int timeSteps = timeGeometry->CountTimeSteps(); for (unsigned int t = 0; t < timeSteps; ++t) { if (IsEmptyTimeStep(t) == false) return false; } return true; } itk::SmartPointer mitk::BaseData::GetSource() const { return static_cast(Superclass::GetSource().GetPointer()); } mitk::PropertyList::Pointer mitk::BaseData::GetPropertyList() const { return m_PropertyList; } mitk::BaseProperty::Pointer mitk::BaseData::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void mitk::BaseData::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::BaseData::SetPropertyList(PropertyList *pList) { m_PropertyList = pList; } void mitk::BaseData::SetOrigin(const mitk::Point3D &origin) { TimeGeometry *timeGeom = GetTimeGeometry(); assert(timeGeom != nullptr); TimeStepType steps = timeGeom->CountTimeSteps(); for (TimeStepType timestep = 0; timestep < steps; ++timestep) { auto geometry = GetGeometry(timestep); if (geometry != nullptr) { geometry->SetOrigin(origin); } } } unsigned long mitk::BaseData::GetMTime() const { unsigned long time = Superclass::GetMTime(); if (m_TimeGeometry.IsNotNull()) { if ((time < m_TimeGeometry->GetMTime())) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::BaseData::Graft(const itk::DataObject *) { itkExceptionMacro(<< "Graft not implemented for mitk::BaseData subclass " << this->GetNameOfClass()) } void mitk::BaseData::CopyInformation(const itk::DataObject *data) { const auto *bd = dynamic_cast(data); if (bd != nullptr) { m_PropertyList = bd->GetPropertyList()->Clone(); if (bd->GetTimeGeometry() != nullptr) { m_TimeGeometry = bd->GetTimeGeometry()->Clone(); } } else { // pointer could not be cast back down; this can be the case if your filters input // and output objects differ in type; then you have to write your own GenerateOutputInformation method itkExceptionMacro(<< "mitk::BaseData::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self *).name()); } } bool mitk::BaseData::IsInitialized() const { return m_Initialized; } void mitk::BaseData::Clear() { this->ClearData(); this->InitializeEmpty(); } void mitk::BaseData::ClearData() { if (m_Initialized) { ReleaseData(); m_Initialized = false; } } void mitk::BaseData::ExecuteOperation(mitk::Operation * /*operation*/) { // empty by default. override if needed! } void mitk::BaseData::PrintSelf(std::ostream &os, itk::Indent indent) const { os << std::endl; os << indent << " TimeGeometry: "; if (GetTimeGeometry() == nullptr) os << "nullptr" << std::endl; else GetTimeGeometry()->Print(os, indent); // print out all properties PropertyList::Pointer propertyList = this->GetPropertyList(); if (propertyList.IsNotNull() && !propertyList->IsEmpty()) { // general headline os << "Properties of BaseData:" << std::endl; const PropertyList::PropertyMap *map = propertyList->GetMap(); for (auto iter = map->begin(); iter != map->end(); ++iter) { os << " " << (*iter).first << " " << (*iter).second->GetValueAsString() << std::endl; } } } mitk::BaseProperty::ConstPointer mitk::BaseData::GetConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) const { if (propertyKey.empty()) return nullptr; if (contextName.empty() || fallBackOnDefaultContext) return m_PropertyList->GetProperty(propertyKey); return nullptr; } mitk::BaseProperty * mitk::BaseData::GetNonConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) return nullptr; if (contextName.empty() || fallBackOnDefaultContext) return m_PropertyList->GetProperty(propertyKey); return nullptr; } void mitk::BaseData::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->SetProperty(propertyKey, property); return; } - mitkThrow() << "Unknown property context."; + mitkThrow() << "Unknown or unsupported non-default property context."; +} + +void mitk::BaseData::RemoveProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) +{ + if (propertyKey.empty()) + mitkThrow() << "Property key is empty."; + + if (contextName.empty() || fallBackOnDefaultContext) + { + m_PropertyList->RemoveProperty(propertyKey); + return; + } + + mitkThrow() << "Unknown or unsupported non-default property context."; } std::vector mitk::BaseData::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const { std::vector propertyKeys; if (contextName.empty() || includeDefaultContext) { for (auto property : *m_PropertyList->GetMap()) propertyKeys.push_back(property.first); } return propertyKeys; } std::vector mitk::BaseData::GetPropertyContextNames() const { return std::vector(); } diff --git a/Modules/Core/src/DataManagement/mitkDataNode.cpp b/Modules/Core/src/DataManagement/mitkDataNode.cpp index 282d143448..e869b482dc 100644 --- a/Modules/Core/src/DataManagement/mitkDataNode.cpp +++ b/Modules/Core/src/DataManagement/mitkDataNode.cpp @@ -1,711 +1,736 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataNode.h" #include "mitkCoreObjectFactory.h" #include #include "mitkGroupTagProperty.h" #include "mitkProperties.h" #include "mitkSmartPointerProperty.h" #include "mitkStringProperty.h" //#include "mitkMaterialProperty.h" #include "mitkColorProperty.h" #include "mitkCoreObjectFactory.h" #include "mitkGenericProperty.h" #include "mitkGeometry3D.h" #include "mitkImageSource.h" #include "mitkLevelWindowProperty.h" #include "mitkRenderingManager.h" mitk::Mapper *mitk::DataNode::GetMapper(MapperSlotId id) const { if ((id >= m_Mappers.size()) || (m_Mappers[id].IsNull())) { if (id >= m_Mappers.capacity()) { // int i, size=id-m_Mappers.capacity()+10; m_Mappers.resize(id + 10); } m_Mappers[id] = CoreObjectFactory::GetInstance()->CreateMapper(const_cast(this), id); } return m_Mappers[id]; } mitk::BaseData *mitk::DataNode::GetData() const { return m_Data; } void mitk::DataNode::SetData(mitk::BaseData *baseData) { if (m_Data != baseData) { m_Mappers.clear(); m_Mappers.resize(10); if (m_Data.IsNotNull() && baseData != nullptr) { // Do previous and new data have same type? Keep existing properties. if (0 == strcmp(m_Data->GetNameOfClass(), baseData->GetNameOfClass())) { m_Data = baseData; } else { m_Data = baseData; this->GetPropertyList()->Clear(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); } } else { m_Data = baseData; mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); } m_DataReferenceChangedTime.Modified(); Modified(); } } mitk::DataNode::DataNode() : m_PropertyList(PropertyList::New()), m_PropertyListModifiedObserverTag(0) { m_Mappers.resize(10); // subscribe for modified event itk::MemberCommand::Pointer _PropertyListModifiedCommand = itk::MemberCommand::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::DataNode::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); } mitk::DataNode::~DataNode() { if (m_PropertyList.IsNotNull()) m_PropertyList->RemoveObserver(m_PropertyListModifiedObserverTag); m_Mappers.clear(); m_Data = nullptr; } mitk::DataNode &mitk::DataNode::operator=(const DataNode &right) { mitk::DataNode *node = mitk::DataNode::New(); node->SetData(right.GetData()); return *node; } mitk::DataNode &mitk::DataNode::operator=(mitk::BaseData *right) { mitk::DataNode *node = mitk::DataNode::New(); node->SetData(right); return *node; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) MBI_STD::istream &mitk::operator>>(MBI_STD::istream &i, mitk::DataNode::Pointer &dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MBI_STD::istream & operator>>(MBI_STD::istream &i, mitk::DataNode::Pointer &dtn) #endif { dtn = mitk::DataNode::New(); // i >> av.get(); return i; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) MBI_STD::ostream &mitk::operator<<(MBI_STD::ostream &o, mitk::DataNode::Pointer &dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MBI_STD::ostream & operator<<(MBI_STD::ostream &o, mitk::DataNode::Pointer &dtn) #endif { if (dtn->GetData() != nullptr) o << dtn->GetData()->GetNameOfClass(); else o << "empty data"; return o; } void mitk::DataNode::SetMapper(MapperSlotId id, mitk::Mapper *mapper) { m_Mappers[id] = mapper; if (mapper != nullptr) mapper->SetDataNode(this); } void mitk::DataNode::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } } void mitk::DataNode::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::DataNode::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::DataNode::VerifyRequestedRegion() { return true; } void mitk::DataNode::SetRequestedRegion(const itk::DataObject * /*data*/) { } mitk::DataNode::PropertyListKeyNames mitk::DataNode::GetPropertyListNames() const { PropertyListKeyNames result; for (auto entries : m_MapOfPropertyLists) result.push_back(entries.first); return result; } void mitk::DataNode::CopyInformation(const itk::DataObject * /*data*/) { } mitk::PropertyList *mitk::DataNode::GetPropertyList(const mitk::BaseRenderer *renderer) const { if (renderer == nullptr) return m_PropertyList; return this->GetPropertyList(renderer->GetName()); } mitk::PropertyList *mitk::DataNode::GetPropertyList(const std::string &rendererName) const { if (rendererName.empty()) return m_PropertyList; mitk::PropertyList::Pointer &propertyList = m_MapOfPropertyLists[rendererName]; if (propertyList.IsNull()) propertyList = mitk::PropertyList::New(); assert(m_MapOfPropertyLists[rendererName].IsNotNull()); return propertyList; } void mitk::DataNode::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty *mitk::DataNode::GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer, bool fallBackOnDataProperties) const { if (nullptr == propertyKey) return nullptr; if (nullptr != renderer) { auto it = m_MapOfPropertyLists.find(renderer->GetName()); if (m_MapOfPropertyLists.end() != it) { auto property = it->second->GetProperty(propertyKey); if (nullptr != property) return property; } } auto property = m_PropertyList->GetProperty(propertyKey); if (nullptr == property && fallBackOnDataProperties && m_Data.IsNotNull()) property = m_Data->GetProperty(propertyKey); return property; } mitk::DataNode::GroupTagList mitk::DataNode::GetGroupTags() const { GroupTagList groups; const PropertyList::PropertyMap *propertyMap = m_PropertyList->GetMap(); for (auto groupIter = propertyMap->begin(); // m_PropertyList is created in the constructor, so we don't check it here groupIter != propertyMap->end(); ++groupIter) { const BaseProperty *bp = groupIter->second; if (dynamic_cast(bp)) { groups.insert(groupIter->first); } } return groups; } bool mitk::DataNode::GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::DataNode::GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer) const { mitk::IntProperty::Pointer intprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::DataNode::GetFloatProperty(const char *propertyKey, float &floatValue, const mitk::BaseRenderer *renderer) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::DataNode::GetDoubleProperty(const char *propertyKey, double &doubleValue, const mitk::BaseRenderer *renderer) const { mitk::DoubleProperty::Pointer doubleprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (doubleprop.IsNull()) { // try float instead float floatValue = 0; if (this->GetFloatProperty(propertyKey, floatValue, renderer)) { doubleValue = floatValue; return true; } return false; } doubleValue = doubleprop->GetValue(); return true; } bool mitk::DataNode::GetStringProperty(const char *propertyKey, std::string &string, const mitk::BaseRenderer *renderer) const { mitk::StringProperty::Pointer stringProp = dynamic_cast(GetProperty(propertyKey, renderer)); if (stringProp.IsNull()) { return false; } else { // memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } bool mitk::DataNode::GetColor(float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); return true; } bool mitk::DataNode::GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (opacityprop.IsNull()) return false; opacity = opacityprop->GetValue(); return true; } bool mitk::DataNode::GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::LevelWindowProperty::Pointer levWinProp = dynamic_cast(GetProperty(propertyKey, renderer)); if (levWinProp.IsNull()) return false; levelWindow = levWinProp->GetLevelWindow(); return true; } void mitk::DataNode::SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetColor( float red, float green, float blue, const mitk::BaseRenderer *renderer, const char *propertyKey) { float color[3]; color[0] = red; color[1] = green; color[2] = blue; SetColor(color, renderer, propertyKey); } void mitk::DataNode::SetColor(const float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetVisibility(bool visible, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetOpacity(float opacity, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetLevelWindow(mitk::LevelWindow levelWindow, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::LevelWindowProperty::Pointer prop; prop = mitk::LevelWindowProperty::New(levelWindow); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::DataNode::SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::DataNode::SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { if (dynamic_cast(this->GetProperty(propertyKey, renderer)) != nullptr) { MITK_WARN << "Setting float property " << propertyKey << " although a double property with the same name already exists"; } GetPropertyList(renderer)->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::DataNode::SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer) { if (dynamic_cast(this->GetProperty(propertyKey, renderer)) != nullptr) { MITK_WARN << "Setting double property " << propertyKey << " although a float property with the same name already exists"; } GetPropertyList(renderer)->SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } void mitk::DataNode::SetStringProperty(const char *propertyKey, const char *stringValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::DataNode::SetProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, propertyValue); } void mitk::DataNode::ReplaceProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->ReplaceProperty(propertyKey, propertyValue); } void mitk::DataNode::AddProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer, bool overwrite) { if ((overwrite) || (GetProperty(propertyKey, renderer) == nullptr)) { SetProperty(propertyKey, propertyValue, renderer); } } vtkLinearTransform *mitk::DataNode::GetVtkTransform(int t) const { assert(m_Data.IsNotNull()); mitk::BaseGeometry *geometry = m_Data->GetGeometry(t); if (geometry == nullptr) return nullptr; return geometry->GetVtkTransform(); } unsigned long mitk::DataNode::GetMTime() const { unsigned long time = Superclass::GetMTime(); if (m_Data.IsNotNull()) { if ((time < m_Data->GetMTime()) || ((m_Data->GetSource().IsNotNull()) && (time < m_Data->GetSource()->GetMTime()))) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::DataNode::SetSelected(bool selected, const mitk::BaseRenderer *renderer) { mitk::BoolProperty::Pointer selectedProperty = dynamic_cast(GetProperty("selected")); if (selectedProperty.IsNull()) { selectedProperty = mitk::BoolProperty::New(); selectedProperty->SetValue(false); SetProperty("selected", selectedProperty, renderer); } if (selectedProperty->GetValue() != selected) { selectedProperty->SetValue(selected); itk::ModifiedEvent event; InvokeEvent(event); } } /* class SelectedEvent : public itk::ModifiedEvent { public: typedef SelectedEvent Self; typedef itk::ModifiedEvent Superclass; SelectedEvent(DataNode* dataNode) { m_DataNode = dataNode; }; DataNode* GetDataNode() { return m_DataNode; }; virtual const char * GetEventName() const { return "SelectedEvent"; } virtual bool CheckEvent(const ::itk::EventObject* e) const { return dynamic_cast(e); } virtual ::itk::EventObject* MakeObject() const { return new Self(m_DataNode); } private: DataNode* m_DataNode; SelectedEvent(const Self& event) { m_DataNode = event.m_DataNode; }; void operator=(const Self& event) { m_DataNode = event.m_DataNode; } }; */ bool mitk::DataNode::IsSelected(const mitk::BaseRenderer *renderer) { bool selected; if (!GetBoolProperty("selected", selected, renderer)) return false; return selected; } void mitk::DataNode::SetDataInteractor(const DataInteractor::Pointer interactor) { if (m_DataInteractor == interactor) return; m_DataInteractor = interactor; this->Modified(); mitk::DataNode::InteractorChangedEvent changedEvent; this->InvokeEvent(changedEvent); } mitk::DataInteractor::Pointer mitk::DataNode::GetDataInteractor() const { return m_DataInteractor; } void mitk::DataNode::PropertyListModified(const itk::Object * /*caller*/, const itk::EventObject &) { Modified(); } mitk::BaseProperty::ConstPointer mitk::DataNode::GetConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) const { if (propertyKey.empty()) return nullptr; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { BaseProperty::ConstPointer property = propertyListIter->second->GetProperty(propertyKey); if (property.IsNotNull()) return property; } } if (contextName.empty() || fallBackOnDefaultContext) { BaseProperty::ConstPointer property = m_PropertyList->GetProperty(propertyKey); - if (property.IsNull() && m_Data.IsNull()) + if (property.IsNull() && m_Data.IsNotNull()) property = m_Data->GetProperty(propertyKey.c_str()); return property; } return nullptr; } mitk::BaseProperty * mitk::DataNode::GetNonConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) return nullptr; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { auto property = propertyListIter->second->GetProperty(propertyKey); if (nullptr != property) return property; } } if (contextName.empty() || fallBackOnDefaultContext) { auto property = m_PropertyList->GetProperty(propertyKey); - if (nullptr == property && m_Data.IsNull()) + if (nullptr == property && m_Data.IsNotNull()) property = m_Data->GetProperty(propertyKey.c_str()); return property; } return nullptr; } void mitk::DataNode::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { propertyListIter->second->SetProperty(propertyKey, property); return; } } if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->SetProperty(propertyKey, property); return; } mitkThrow() << "Unknown property context."; } +void mitk::DataNode::RemoveProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) +{ + if (propertyKey.empty()) + mitkThrow() << "Property key is empty."; + + if (!contextName.empty()) + { + auto propertyListIter = m_MapOfPropertyLists.find(contextName); + + if (m_MapOfPropertyLists.end() != propertyListIter) + { + propertyListIter->second->RemoveProperty(propertyKey); + return; + } + } + + if (contextName.empty() || fallBackOnDefaultContext) + { + m_PropertyList->RemoveProperty(propertyKey); + return; + } + + mitkThrow() << "Unknown property context."; +} + std::vector mitk::DataNode::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const { std::vector propertyKeys; if (contextName.empty()) { for (auto property : *m_PropertyList->GetMap()) propertyKeys.push_back(property.first); return propertyKeys; } auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { for (auto property : *propertyListIter->second->GetMap()) propertyKeys.push_back(property.first); } if (includeDefaultContext) { for (auto property : *m_PropertyList->GetMap()) { auto propertyKeyIter = std::find(propertyKeys.begin(), propertyKeys.end(), property.first); if (propertyKeys.end() == propertyKeyIter) propertyKeys.push_back(property.first); } } return propertyKeys; } std::vector mitk::DataNode::GetPropertyContextNames() const { return this->GetPropertyListNames(); } diff --git a/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp b/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp new file mode 100644 index 0000000000..d3e6a100c0 --- /dev/null +++ b/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp @@ -0,0 +1,81 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkGenericIDRelationRule.h" + +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::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 +{ + // 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; +}; + +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 { + // 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 new file mode 100644 index 0000000000..5bb52f0556 --- /dev/null +++ b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp @@ -0,0 +1,580 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkPropertyRelationRuleBase.h" + +#include +#include +#include +#include +#include + +#include +#include + +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"); +}; + +mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRuleRootKeyPath() const +{ + return GetRootKeyPath().AddElement(this->GetRuleID()); +}; + +bool mitk::PropertyRelationRuleBase::IsSource(const IPropertyProvider *owner) const +{ + if (!owner) + { + mitkThrow() << "Error. Passed owner pointer is NULL"; + } + + auto keys = owner->GetPropertyKeys(); + + auto rootkey = PropertyKeyPathToPropertyName(this->GetRuleRootKeyPath()); + + for (const auto &key : keys) + { + if (key.find(rootkey) == 0) + { + return true; + } + } + + return false; +}; + +mitk::PropertyRelationRuleBase::RelationType mitk::PropertyRelationRuleBase::HasRelation( + 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) != NULL_INSTANCE_ID()) + { // has relations of type Connected_ID; + result = RelationType::Connected_ID; + } + + if (result == RelationType::None) + { + 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(); + } + + if (!instanceIDs_data.empty() && instanceIDs_data.front() != NULL_INSTANCE_ID()) + { // has relations of type Connected_Data; + result = RelationType::Connected_Data; + } + } + + if (result == RelationType::None) + { + if (this->HasImplicitDataRelation(source, destination)) + { // has relations of type Connected_Data; + result = RelationType::Implicit_Data; + } + } + + return result; +}; + +mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetExistingRelations( + const IPropertyProvider *source) const +{ + if (!source) + { + mitkThrow() << "Error. Passed source pointer is NULL"; + } + + auto relIDRegExStr = + PropertyKeyPathToPropertyRegEx(this->GetRuleRootKeyPath().AddAnyElement().AddElement("relationUID")); + auto regEx = std::regex(relIDRegExStr); + + const auto keys = source->GetPropertyKeys(); + RelationUIDVectorType relationUIDs; + + for (const auto &key : keys) + { + if (std::regex_match(key, regEx)) + { + auto idProp = source->GetConstProperty(key); + relationUIDs.push_back(idProp->GetValueAsString()); + } + } + + return relationUIDs; +}; + +mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUID( + 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"; + } + + RelationUIDType result; + + auto instanceID = this->GetInstanceID_IDLayer(source, destination); + + if (instanceID == NULL_INSTANCE_ID()) + { + auto instanceID_data = this->GetInstanceID_datalayer(source, destination); + + if (!instanceID_data.empty()) + { + instanceID = instanceID_data.front(); + } + + if (instanceID_data.size() > 1) + { + MITK_WARN << "Property relation on data level is ambigious. First relation is used. Instance ID: " << instanceID; + } + } + + return this->GetRelationUIDByInstanceID(source, instanceID); +}; + +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->GetRuleRootKeyPath().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; + + auto destRegExStr = + PropertyKeyPathToPropertyRegEx(this->GetRuleRootKeyPath().AddAnyElement().AddElement("relationUID")); + 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 idProp = source->GetConstProperty(key); + if (idProp->GetValueAsString() == relationUID) + { + if (instance_matches.size()>1) + { + result = instance_matches[1]; + break; + } + } + } + } + + return result; +}; + +mitk::PropertyRelationRuleBase::InstanceIDType 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); + + std::string result; + + if (identifiable) + { // check for relations of type Connected_ID; + + auto destRegExStr = + PropertyKeyPathToPropertyRegEx(this->GetRuleRootKeyPath().AddAnyElement().AddElement("destinationUID")); + auto regEx = std::regex(destRegExStr); + std::smatch instance_matches; + + auto destUID = identifiable->GetUID(); + + auto keys = source->GetPropertyKeys(); + + 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) + { + result = instance_matches[1]; + break; + } + } + } + } + } + + return result; +}; + +void 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"; + } + + InstanceIDType instanceID = this->GetInstanceID_IDLayer(source, destination); + bool hasIDlayer = instanceID != NULL_INSTANCE_ID(); + + 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(); + + if (hasIDlayer && hasDatalayer && instanceID != instanceIDs_data.front()) + { + mitkThrow() << "Property relation information is in an invalid state. ID and data layer point to different " + "relation instances. Rule: " + << this->GetRuleID() << "; ID based instance: " << instanceID + << "; Data base instance: " << instanceIDs_data.front(); + } + + RelationUIDType relationUID = this->CreateRelationUID(); + + if (!hasIDlayer) + { + if (hasDatalayer) + { + instanceID = instanceIDs_data.front(); + } + else + { + instanceID = this->CreateNewRelationInstance(source, relationUID); + } + } + + auto relUIDKey = + PropertyKeyPathToPropertyName(this->GetRuleRootKeyPath().AddElement(instanceID).AddElement("relationUID")); + source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); + + if (!hasIDlayer) + { + auto identifiable = dynamic_cast(destination); + + if (identifiable) + { + auto destUIDKey = + PropertyKeyPathToPropertyName(this->GetRuleRootKeyPath().AddElement(instanceID).AddElement("destinationUID")); + source->SetProperty(destUIDKey, mitk::StringProperty::New(identifiable->GetUID())); + } + } + + if (!hasDatalayer) + { + this->Connect_datalayer(source, destination, instanceID); + } +}; + +void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, const IPropertyProvider *destination) const +{ + try + { + this->Disconnect(source, this->GetRelationUID(source, destination)); + } + catch (const NoPropertyRelationException &) + { + // nothing to do and no real error in context of disconnect. + } +}; + +void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, RelationUIDType relationUID) const +{ + auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); + + if (instanceID != NULL_INSTANCE_ID()) + { + this->Disconnect_datalayer(source, instanceID); + + auto instancePrefix = PropertyKeyPathToPropertyName(this->GetRuleRootKeyPath().AddElement(instanceID)); + + auto keys = source->GetPropertyKeys(); + + 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->GetRuleRootKeyPath().AddAnyElement().AddElement("destinationUID")); + 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)) + { + 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->GetRuleRootKeyPath().AddElement(newID).AddElement("relationUID")); + source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); + + return newID; +}; + +itk::LightObject::Pointer mitk::PropertyRelationRuleBase::InternalClone() const +{ + return Superclass::InternalClone(); +}; + + +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) + + virtual ~NodePredicateRuleFunction() = default; + + virtual 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 +{ + if (!destination) + { + mitkThrow() << "Error. Passed destination pointer is NULL"; + } + + auto check = [destination, minimalRelation](const mitk::IPropertyProvider *node, + const mitk::PropertyRelationRuleBase *rule) { + return rule->HasRelation(node, destination) >= minimalRelation; + }; + + return NodePredicateRuleFunction::New(check, this).GetPointer(); +}; + +mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationsDetector( + const IPropertyProvider *source, RelationType minimalRelation) const +{ + if (!source) + { + mitkThrow() << "Error. Passed source pointer is NULL"; + } + + auto check = [source, minimalRelation](const mitk::IPropertyProvider *node, + const mitk::PropertyRelationRuleBase *rule) { + return rule->HasRelation(source, node) >= minimalRelation; + }; + + 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 + { + return rule->GetRelationUID(source, node) == relationUID; + } + catch(const NoPropertyRelationException &) + { + return false; + } + }; + + return NodePredicateRuleFunction::New(check, this).GetPointer(); +}; diff --git a/Modules/Core/test/files.cmake b/Modules/Core/test/files.cmake index 6630393e3f..258a1d2183 100644 --- a/Modules/Core/test/files.cmake +++ b/Modules/Core/test/files.cmake @@ -1,201 +1,203 @@ # tests with no extra command line parameter set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## DISABLED TESTS ################################################# #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesnt exist any more #mitkStateMachineContainerTest.cpp #rewrite test, indirect since no longer exported Bug 14529 #mitkRegistrationBaseTest.cpp #tested class mitkRegistrationBase doesn't exist any more #mitkSegmentationInterpolationTest.cpp #file doesn't exist! #mitkPipelineSmartPointerCorrectnessTest.cpp #file doesn't exist! #mitkITKThreadingTest.cpp #test outdated because itk::Semaphore was removed from ITK #mitkAbstractTransformPlaneGeometryTest.cpp #mitkVtkAbstractTransformPlaneGeometry doesn't exist any more #mitkTestUtilSharedLibrary.cpp #Linker problem with this test... #mitkTextOverlay2DSymbolsRenderingTest.cpp #Implementation of the tested feature is not finished yet. Ask Christoph or see bug 15104 for details. ################# RUNNING TESTS ################################################### mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkDataNodeTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkFileReaderRegistryTest.cpp #mitkFileWriterRegistryTest.cpp mitkFloatToStringTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataIOTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkImageCastTest.cpp mitkImageEqualTest.cpp mitkImageDataItemTest.cpp mitkImageGeneratorTest.cpp mitkIOUtilTest.cpp mitkBaseDataTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetOnEmptyTest.cpp mitkPointSetLocaleTest.cpp mitkPointSetWriterTest.cpp mitkPointSetReaderTest.cpp mitkPointSetPointOperationsTest.cpp mitkProgressBarTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp mitkPropertyPersistenceTest.cpp mitkPropertyPersistenceInfoTest.cpp + mitkPropertyRelationRuleBaseTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkProportionalTimeGeometryTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp mitkStepperTest.cpp mitkRenderingManagerTest.cpp mitkCompositePixelValueToStringTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkNodePredicateDataPropertyTest.cpp mitkNodePredicateFunctionTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp mitkPropertyKeyPathTest.cpp mitkTinyXMLTest.cpp mitkRawImageFileReaderTest.cpp mitkInteractionEventTest.cpp mitkLookupTableTest.cpp mitkSTLFileReaderTest.cpp mitkPointTypeConversionTest.cpp mitkVectorTypeConversionTest.cpp mitkMatrixTypeConversionTest.cpp mitkArrayTypeConversionTest.cpp mitkSurfaceToImageFilterTest.cpp mitkBaseGeometryTest.cpp mitkImageToSurfaceFilterTest.cpp mitkEqualTest.cpp mitkLineTest.cpp mitkArbitraryTimeGeometryTest mitkItkImageIOTest.cpp mitkRotatedSlice4DTest.cpp mitkLevelWindowManagerCppUnitTest.cpp mitkVectorPropertyTest.cpp mitkTemporoSpatialStringPropertyTest.cpp mitkPropertyNameHelperTest.cpp mitkNodePredicateGeometryTest.cpp mitkPreferenceListReaderOptionsFunctorTest.cpp + mitkGenericIDRelationRuleTest.cpp ) if(MITK_ENABLE_RENDERING_TESTING) set(MODULE_TESTS ${MODULE_TESTS} mitkPlaneGeometryDataMapper2DTest.cpp mitkPointSetDataInteractorTest.cpp #since mitkInteractionTestHelper is currently creating a vtkRenderWindow mitkSurfaceVtkMapper2DTest.cpp #new rendering test in CppUnit style mitkSurfaceVtkMapper2D3DTest.cpp # comparisons/consistency 2D/3D ) endif() # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces ) # list of images for which the tests are run set(MODULE_TESTIMAGE US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACE binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS mitkDataStorageTest.cpp mitkDicomSeriesReaderTest.cpp mitkDICOMLocaleTest.cpp mitkDataNodeTest.cpp mitkEventConfigTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DOpacityTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkSurfaceVtkMapper3DTest mitkSurfaceVtkMapper3DTexturedSphereTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkVTKRenderWindowSizeTest.cpp mitkMultiComponentImageDataComparisonFilterTest.cpp mitkImageToItkTest.cpp mitkImageSliceSelectorTest.cpp mitkSurfaceDepthPeelingTest.cpp ) # Currently not working on windows because of a rendering timing issue # see bug 18083 for details if(NOT WIN32) set(MODULE_CUSTOM_TESTS ${MODULE_CUSTOM_TESTS} mitkSurfaceDepthSortingTest.cpp) endif() set(RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) diff --git a/Modules/Core/test/mitkGenericIDRelationRuleTest.cpp b/Modules/Core/test/mitkGenericIDRelationRuleTest.cpp new file mode 100644 index 0000000000..88da5c31f2 --- /dev/null +++ b/Modules/Core/test/mitkGenericIDRelationRuleTest.cpp @@ -0,0 +1,92 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkGenericIDRelationRule.h" + +#include "mitkDataNode.h" +#include "mitkStringProperty.h" + +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" + +class mitkGenericIDRelationRuleTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkGenericIDRelationRuleTestSuite); + + MITK_TEST(GetRuleID); + MITK_TEST(GetDisplayName); + MITK_TEST(GetSourceRoleName); + MITK_TEST(GetDestinationRoleName); + MITK_TEST(Clone); + + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::GenericIDRelationRule::Pointer rule_new1; + mitk::GenericIDRelationRule::Pointer rule_new2; + mitk::GenericIDRelationRule::Pointer rule_new4; + +public: + void setUp() override + { + rule_new1 = mitk::GenericIDRelationRule::New("TestID"); + rule_new2 = mitk::GenericIDRelationRule::New("TestID2", "display name"); + rule_new4 = mitk::GenericIDRelationRule::New("TestID4", "display name 4", "srole", "drole"); + } + + void tearDown() override {} + + void GetRuleID() + { + CPPUNIT_ASSERT(rule_new1->GetRuleID() == "IDRelation_TestID"); + CPPUNIT_ASSERT(rule_new2->GetRuleID() == "IDRelation_TestID2"); + CPPUNIT_ASSERT(rule_new4->GetRuleID() == "IDRelation_TestID4"); + } + + void GetDisplayName() + { + CPPUNIT_ASSERT(rule_new1->GetDisplayName() == "TestID relation"); + CPPUNIT_ASSERT(rule_new2->GetDisplayName() == "display name"); + CPPUNIT_ASSERT(rule_new4->GetDisplayName() == "display name 4"); + } + + void GetSourceRoleName() + { + CPPUNIT_ASSERT(rule_new1->GetSourceRoleName() == "source of TestID relation"); + CPPUNIT_ASSERT(rule_new2->GetSourceRoleName() == "source of TestID2 relation"); + CPPUNIT_ASSERT(rule_new4->GetSourceRoleName() == "srole"); + } + + void GetDestinationRoleName() + { + CPPUNIT_ASSERT(rule_new1->GetDestinationRoleName() == "destination of TestID relation"); + CPPUNIT_ASSERT(rule_new2->GetDestinationRoleName() == "destination of TestID2 relation"); + CPPUNIT_ASSERT(rule_new4->GetDestinationRoleName() == "drole"); + } + + void Clone() + { + auto clone = rule_new4->Clone(); + + CPPUNIT_ASSERT(clone->GetRuleID() == "IDRelation_TestID4"); + CPPUNIT_ASSERT(clone->GetDisplayName() == "display name 4"); + CPPUNIT_ASSERT(clone->GetSourceRoleName() == "srole"); + CPPUNIT_ASSERT(clone->GetDestinationRoleName() == "drole"); + } + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkGenericIDRelationRule) diff --git a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp new file mode 100644 index 0000000000..3a4815c20f --- /dev/null +++ b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp @@ -0,0 +1,641 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkPropertyRelationRuleBase.h" + +#include "mitkDataNode.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; + + virtual RuleIDType GetRuleID() const override { return "TestRule"; }; + + virtual std::string GetDisplayName() const override { return "TestDisplayName"; } + virtual std::string GetSourceRoleName() const override { return "source role"; } + virtual std::string GetDestinationRoleName() const override { return "destination role"; } + + using Superclass::GetRuleRootKeyPath; + + protected: + TestRule() = default; + virtual ~TestRule() = default; + + using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; + using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; + + virtual InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, + const IPropertyProvider *destination) const override + { + InstanceIDVectorType result; + + auto destProp = destination->GetConstProperty("name"); + + if (destProp.IsNotNull()) + { + auto destRegExStr = + PropertyKeyPathToPropertyRegEx(this->GetRuleRootKeyPath().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]); + } + } + } + } + } + return result; + }; + + virtual bool HasImplicitDataRelation(const IPropertyProvider *source, + const IPropertyProvider *destination) const override + { + auto destProp = destination->GetConstProperty("name"); + auto sourceProp = source->GetConstProperty("referencedName"); + + return destProp.IsNotNull() && sourceProp.IsNotNull() && + destProp->GetValueAsString() == sourceProp->GetValueAsString(); + }; + + virtual 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(this->GetRuleRootKeyPath().AddElement(instanceID).AddElement("dataHandle")), + StringProperty::New(destProp->GetValueAsString())); + }; + + virtual void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType & /*instanceID*/) const override + { + source->RemoveProperty("referencedName"); + }; + }; +} // namespace mitk + +class mitkPropertyRelationRuleBaseTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkPropertyRelationRuleBaseTestSuite); + + MITK_TEST(IsSourceCandidate); + MITK_TEST(IsDestinationCandidate); + MITK_TEST(IsSource); + MITK_TEST(HasRelation); + MITK_TEST(GetExistingRelations); + MITK_TEST(GetRelationUID); + MITK_TEST(GetSourceCandidateIndicator); + MITK_TEST(GetDestinationCandidateIndicator); + MITK_TEST(GetConnectedSourcesDetector); + MITK_TEST(GetSourcesDetector); + MITK_TEST(GetDestinationsDetector); + MITK_TEST(GetDestinationDetector); + MITK_TEST(Connect); + MITK_TEST(Disconnect); + + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::TestRule::Pointer rule; + + mitk::DataNode::Pointer unRelated; + 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 dest_1; + mitk::DataNode::Pointer dest_2; + + bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const + { + auto keyPath = rule->GetRuleRootKeyPath(); + 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(); + + unRelated = mitk::DataNode::New(); + unRelated->SetName("unRelated"); + + dest_1 = mitk::DataNode::New(); + dest_1->SetName("dest_1"); + + dest_2 = mitk::DataNode::New(); + dest_2->SetName("dest_2"); + + 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." + rule->GetRuleID() + ".1.relationUID"; + source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid1")); + name = "MITK.Relations." + rule->GetRuleID() + ".1.destinationUID"; + source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); + + source_data_1 = source_implicit_1->Clone(); + name = "MITK.Relations." + rule->GetRuleID() + ".1.relationUID"; + source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid2")); + name = "MITK.Relations." + rule->GetRuleID() + ".1.dataHandle"; + source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); + + source_1 = source_data_1->Clone(); + name = "MITK.Relations." + rule->GetRuleID() + ".1.relationUID"; + source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid3"), nullptr, true); + name = "MITK.Relations." + rule->GetRuleID() + ".1.destinationUID"; + source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); + + source_multi = source_1->Clone(); + name = "MITK.Relations." + rule->GetRuleID() + ".1.relationUID"; + source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid4"), nullptr, true); + name = "MITK.Relations." + rule->GetRuleID() + ".1.destinationUID"; + source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); + name = "MITK.Relations." + rule->GetRuleID() + ".4.relationUID"; + source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid5")); + name = "MITK.Relations." + rule->GetRuleID() + ".4.destinationUID"; + source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2->GetUID())); + name = "MITK.Relations." + rule->GetRuleID() + ".2.relationUID"; + source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid6")); + name = "MITK.Relations." + rule->GetRuleID() + ".2.destinationUID"; + source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("unknown")); + + source_otherRule = mitk::DataNode::New(); + name = "MITK.Relations.otherRuleID.1.relationUID"; + source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid7")); + name = "MITK.Relations.otherRuleID.1.destinationUID"; + source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); + } + + void tearDown() override {} + + 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_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)); + } + + 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_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); + } + + 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_implicit_1).empty()); + + auto 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()); + } + + void GetRelationUID() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->GetRelationUID(nullptr, dest_1), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->GetRelationUID(source_1, nullptr), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE( + "Violated precondition (no relation of connected_data or conncected_ID) does not throw.", + rule->GetRelationUID(source_1, unRelated), + mitk::NoPropertyRelationException); + + CPPUNIT_ASSERT_THROW_MESSAGE( + "Violated precondition (no relation of connected_data or conncected_ID) does not throw.", + rule->GetRelationUID(unRelated, dest_1), + mitk::NoPropertyRelationException); + + CPPUNIT_ASSERT_THROW_MESSAGE( + "Violated precondition (no relation of connected_data or conncected_ID) does not throw.", + rule->GetRelationUID(source_otherRule, dest_1), + mitk::NoPropertyRelationException); + + CPPUNIT_ASSERT(rule->GetRelationUID(source_idOnly_1, dest_1) == "uid1"); + CPPUNIT_ASSERT(rule->GetRelationUID(source_data_1, dest_1) == "uid2"); + CPPUNIT_ASSERT(rule->GetRelationUID(source_1, dest_1) == "uid3"); + CPPUNIT_ASSERT(rule->GetRelationUID(source_multi, dest_1) == "uid4"); + CPPUNIT_ASSERT(rule->GetRelationUID(source_multi, dest_2) == "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_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)); + } + + 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_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); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); + + 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_ID); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); + + 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); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); + + 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_implicit_1); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + 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); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + 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)); + predicate = + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + 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); + 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); + 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); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); + predicate = + rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); + } + + 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); + rule->Connect(source_implicit_1, dest_1); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + + // check upgrade of an data connection + 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); + std::string name = "MITK.Relations." + rule->GetRuleID() + ".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 not stored.", prop->GetValueAsString() == dest_1->GetUID()); + + // 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_MESSAGE("Additional relation was defined instead of updating exting one.", + rule->GetExistingRelations(source_1).size() == 1); + name = "MITK.Relations." + rule->GetRuleID() + ".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 was 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 not 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_MESSAGE("Additional relation was defined instead of updating exting one.", + rule->GetExistingRelations(source_1).size() == 1); + + // check new connection + rule->Connect(source_multi, unRelated); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, unRelated) == + mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + name = "MITK.Relations." + rule->GetRuleID() + ".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 not 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 not stored.", + prop->GetValueAsString() == unRelated->GetName()); + name = "MITK.Relations." + rule->GetRuleID() + ".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 not stored.", prop->GetValueAsString() == unRelated->GetUID()); + } + + 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); + 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")); + + rule->Disconnect(source_1, dest_2); + 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); + rule->Disconnect(source_1, dest_1); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!this->hasRelationProperties(source_1)); + + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->Disconnect(source_multi, dest_1); + 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")); + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkPropertyRelationRuleBase)