diff --git a/Core/Code/IO/mitkFileWriterRegistry.cpp b/Core/Code/IO/mitkFileWriterRegistry.cpp index cf4e2fc972..58a43818df 100644 --- a/Core/Code/IO/mitkFileWriterRegistry.cpp +++ b/Core/Code/IO/mitkFileWriterRegistry.cpp @@ -1,202 +1,206 @@ /*=================================================================== 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 "mitkFileWriterRegistry.h" // MITK #include +// ITK +#include + // Microservices #include #include #include #include mitk::FileWriterRegistry::FileWriterRegistry() { } mitk::FileWriterRegistry::~FileWriterRegistry() { for (std::map >::iterator iter = m_ServiceObjects.begin(), - end = m_ServiceObjects.end(); iter != end; ++iter) + end = m_ServiceObjects.end(); iter != end; ++iter) { iter->second.UngetService(iter->first); } } //////////////////// WRITING DIRECTLY //////////////////// void mitk::FileWriterRegistry::Write(const mitk::BaseData* data, const std::string& path, us::ModuleContext* context) { // Find extension - std::string extension = path; - extension.erase(0, path.find_last_of('.')); + std::string extension = itksys::SystemTools::GetFilenameExtension(path); + + // Get best Writer + mitk::IFileWriter* writer = GetWriter(data, extension, mitk::IFileWriter::OptionNames(), context); + // Throw exception if no compatible Writer was found + if (writer == NULL) + { + mitkThrow() << "Tried to directly write a file of type '" << data->GetNameOfClass() + << "' and extension '" << extension + << "' via FileWriterRegistry, but no writer supporting this filetype was found."; + } + writer->Write(data, path); +} +void mitk::FileWriterRegistry::Write(const mitk::BaseData* data, std::ostream& os, us::ModuleContext* context) +{ // Get best Writer - mitk::IFileWriter* Writer = GetWriter(extension, context); + mitk::IFileWriter* writer = GetWriter(data, std::string(), mitk::IFileWriter::OptionNames(), context); // Throw exception if no compatible Writer was found - if (Writer == NULL) mitkThrow() << "Tried to directly Write a file of type '" + extension + "' via FileWriterRegistry, but no Writer supporting this filetype was found."; - Writer->Write(data, path); + if (writer == NULL) + { + mitkThrow() << "Tried to directly write a file of type '" << data->GetNameOfClass() + << "' to a std::ostream via FileWriterRegistry, but no writer supporting this BaseData type was found."; + } + writer->Write(data, os); } + //////////////////// GETTING WRITERS //////////////////// -mitk::IFileWriter* mitk::FileWriterRegistry::GetWriter(const std::string& extension, us::ModuleContext* context ) +mitk::IFileWriter* mitk::FileWriterRegistry::GetWriter( + const mitk::BaseData* baseData, const std::string& extension, + const mitk::IFileWriter::OptionNames& options, us::ModuleContext* context) { - std::vector results = GetWriters(extension, context); + return GetWriter(baseData->GetNameOfClass(), extension, options, context); +} + +mitk::IFileWriter* mitk::FileWriterRegistry::GetWriter( + const std::string& baseDataType, const std::string& extension, + const mitk::IFileWriter::OptionNames& options, us::ModuleContext* context) +{ + std::vector results = GetWriters(baseDataType, extension, options, context); if (results.empty()) return NULL; return results.front(); } -std::vector mitk::FileWriterRegistry::GetWriters(const std::string& extension, us::ModuleContext* context ) +std::vector mitk::FileWriterRegistry::GetWriters( + const mitk::BaseData* baseData, const std::string& extension, + const mitk::IFileWriter::OptionNames& options, us::ModuleContext* context) +{ + return GetWriters(baseData->GetNameOfClass(), extension, options, context); +} + +std::vector mitk::FileWriterRegistry::GetWriters( + const std::string& baseDataType, const std::string& extension, + const mitk::IFileWriter::OptionNames& options, us::ModuleContext* context) { - std::vector result; - const std::vector > refs = GetWriterList(extension, context); + // filter for class and extension + std::string filter = us::LDAPProp(mitk::IFileWriter::PROP_BASEDATA_TYPE) == baseDataType && + us::LDAPProp(mitk::IFileWriter::PROP_EXTENSION) == extension; + + std::vector > refs = context->GetServiceReferences(filter); + std::sort(refs.begin(), refs.end()); + std::reverse(refs.begin(), refs.end()); + + std::vector result; result.reserve(refs.size()); // Translate List of ServiceRefs to List of Pointers for (std::vector >::const_iterator iter = refs.begin(), end = refs.end(); - iter != end; ++iter) + iter != end; ++iter) { us::ServiceObjects serviceObjects = context->GetServiceObjects(*iter); mitk::IFileWriter* writer = serviceObjects.GetService(); - m_ServiceObjects.insert(std::make_pair(writer, serviceObjects)); - result.push_back(writer); - } - return result; -} - -mitk::IFileWriter* mitk::FileWriterRegistry::GetWriter(const std::string& extension, const std::list& options, - us::ModuleContext* context ) -{ - const std::vector matching = mitk::FileWriterRegistry::GetWriters(extension, options, context); - if (matching.empty()) return NULL; - return matching.front(); -} - -std::vector mitk::FileWriterRegistry::GetWriters(const std::string& extension, const std::list& options, us::ModuleContext* context ) -{ - const std::vector allWriters = mitk::FileWriterRegistry::GetWriters(extension, context); - std::vector result; - // the list is always sorted by priority. Now find Writer that supports all options - - for (std::vector ::const_iterator iter = allWriters.begin(), end = allWriters.end(); - iter != end; ++iter) - { // Now see if this Writer supports all options. If yes, push to results - if ( mitk::FileWriterRegistry::WriterSupportsOptions(*iter, options) ) + if (mitk::FileWriterRegistry::WriterSupportsOptions(writer, options)) { - result.push_back(*iter); + m_ServiceObjects.insert(std::make_pair(writer, serviceObjects)); + result.push_back(writer); } } + return result; } + //////////////////// GENERIC INFORMATION //////////////////// std::string mitk::FileWriterRegistry::GetSupportedExtensions(const std::string& extension, us::ModuleContext* context) { - const std::vector > refs = GetWriterList(extension, context); + std::string filter = us::LDAPProp(mitk::IFileWriter::PROP_EXTENSION) == extension; + const std::vector > refs = context->GetServiceReferences(filter); return CreateFileDialogString(refs); } -std::string mitk::FileWriterRegistry::GetSupportedWriters(const std::string& basedataType, us::ModuleContext* context) +std::string mitk::FileWriterRegistry::GetSupportedWriters(const std::string& baseDataType, us::ModuleContext* context) { - const std::vector > refs = GetWriterListByBasedataType(basedataType, context); + std::string filter = us::LDAPProp(mitk::IFileWriter::PROP_BASEDATA_TYPE) == baseDataType; + const std::vector > refs = context->GetServiceReferences(filter); return CreateFileDialogString(refs); } + //////////////////// INTERNAL CODE //////////////////// bool mitk::FileWriterRegistry::WriterSupportsOptions(mitk::IFileWriter* writer, const mitk::IFileWriter::OptionNames& options ) { const mitk::IFileWriter::OptionList writerOptions = writer->GetOptions(); if (options.empty()) return true; // if no options were requested, return true unconditionally if (writerOptions.empty()) return false; // if options were requested and reader supports no options, return false // For each of the strings in requested options, check if option is available in reader for(mitk::IFileWriter::OptionNames::const_iterator options_i = options.begin(), i_end = options.end(); options_i != i_end; ++options_i) { { bool optionFound = false; // Iterate over each available option from reader to check if one of them matches the current option for(mitk::IFileWriter::OptionList::const_iterator options_j = writerOptions.begin(), j_end = writerOptions.end(); options_j != j_end; ++options_j) { if ( *options_i == options_j->first ) optionFound = true; } if (optionFound == false) return false; // If one option was not found, leave method and return false } } return true; // if all options have been found, return true } std::string mitk::FileWriterRegistry::CreateFileDialogString(const std::vector >& refs) { std::vector entries; // Will contain Description + Extension (Human readable) entries.reserve(refs.size()); std::string knownExtensions; // Will contain plain list of all known extensions (for the QFileDialog entry "All Known Extensions") for (std::vector >::const_iterator iterator = refs.begin(), end = refs.end(); - iterator != end; ++iterator) + iterator != end; ++iterator) { // Generate List of Extensions if (iterator == refs.begin()) // First entry without semicolon knownExtensions += "*" + iterator->GetProperty(mitk::IFileWriter::PROP_EXTENSION).ToString(); else // Ad semicolon for each following entry knownExtensions += "; *" + iterator->GetProperty(mitk::IFileWriter::PROP_EXTENSION).ToString(); // Generate List of human readable entries composed of Description + Extension std::string entry = iterator->GetProperty(mitk::IFileWriter::PROP_DESCRIPTION).ToString() + "(*" + iterator->GetProperty(mitk::IFileWriter::PROP_EXTENSION).ToString() + ");;"; entries.push_back(entry); } std::sort(entries.begin(), entries.end()); std::string result = "Known Extensions (" + knownExtensions + ");;All (*)"; for (std::vector::const_iterator iterator = entries.begin(), end = entries.end(); - iterator != end; ++iterator) + iterator != end; ++iterator) { result += ";;" + *iterator; } return result; } - -//////////////////// uS-INTERACTION //////////////////// - -std::vector< us::ServiceReference > mitk::FileWriterRegistry::GetWriterList(const std::string& extension, us::ModuleContext* context ) -{ - // filter for class and extension - std::string filter; - if (!extension.empty()) - { - filter = "(" + mitk::IFileWriter::PROP_EXTENSION + "=" + extension + ")"; - } - std::vector > result = context->GetServiceReferences(filter); - std::sort(result.begin(), result.end()); - std::reverse(result.begin(), result.end()); - return result; -} - -std::vector< us::ServiceReference > mitk::FileWriterRegistry::GetWriterListByBasedataType(const std::string& basedataType, us::ModuleContext* context ) -{ - // filter for class and extension - std::string filter = us::LDAPProp(mitk::IFileWriter::PROP_BASEDATA_TYPE) == basedataType; - std::vector > result = context->GetServiceReferences(filter); - std::sort(result.begin(), result.end()); - std::reverse(result.begin(), result.end()); - return result; -} diff --git a/Core/Code/IO/mitkFileWriterRegistry.h b/Core/Code/IO/mitkFileWriterRegistry.h index 116e6c23ae..ddcc78a231 100644 --- a/Core/Code/IO/mitkFileWriterRegistry.h +++ b/Core/Code/IO/mitkFileWriterRegistry.h @@ -1,99 +1,108 @@ /*=================================================================== 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 FileWriterRegistry_H_HEADER_INCLUDED_C1E7E521 #define FileWriterRegistry_H_HEADER_INCLUDED_C1E7E521 #include // Microservices #include #include #include #include "mitkIFileWriter.h" // Temporarily disable warning until PIMPL pattern is implemented here #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4251) #endif namespace mitk { class BaseData; } namespace mitk { /** * @ingroup Process * * Provides convenient access to mitk::IFileWriter instances and writing * files from mitk::BaseData types. * * \note The life-time of all mitk::IFileWriter objects returned by an * instance of this class ends with the destruction of that instance. */ class MITK_CORE_EXPORT FileWriterRegistry { public: FileWriterRegistry(); ~FileWriterRegistry(); void Write(const mitk::BaseData* data, const std::string& path, us::ModuleContext* context = us::GetModuleContext()); + void Write(const mitk::BaseData* data, std::ostream& os, us::ModuleContext* context = us::GetModuleContext()); + /** * Returns a compatible Writer to the given file extension */ - mitk::IFileWriter* GetWriter(const std::string& extension, us::ModuleContext* context = us::GetModuleContext() ); + mitk::IFileWriter* GetWriter(const std::string& baseDataType, const std::string& extension = std::string(), + const mitk::IFileWriter::OptionNames& options = mitk::IFileWriter::OptionNames(), + us::ModuleContext* context = us::GetModuleContext() ); - mitk::IFileWriter* GetWriter(const std::string& extension, const std::list& options, us::ModuleContext* context = us::GetModuleContext() ); + mitk::IFileWriter* GetWriter(const mitk::BaseData* baseData, const std::string& extension = std::string(), + const mitk::IFileWriter::OptionNames& options = mitk::IFileWriter::OptionNames(), + us::ModuleContext* context = us::GetModuleContext() ); - std::vector GetWriters(const std::string& extension, us::ModuleContext* context = us::GetModuleContext() ); + std::vector GetWriters(const std::string& baseDataType, const std::string& extension = std::string(), + const mitk::IFileWriter::OptionNames& options = mitk::IFileWriter::OptionNames(), + us::ModuleContext* context = us::GetModuleContext() ); - std::vector GetWriters(const std::string& extension, const std::list& options, us::ModuleContext* context = us::GetModuleContext() ); + std::vector GetWriters(const mitk::BaseData* baseData, const std::string& extension = std::string(), + const mitk::IFileWriter::OptionNames& options = mitk::IFileWriter::OptionNames(), + us::ModuleContext* context = us::GetModuleContext() ); void UngetWriter(mitk::IFileWriter* writer); void UngetWriters(const std::vector& writers); + // TODO: We should not generate GUI dependent strings here. Maybe just return a pair of extensions + // and descriptions. std::string GetSupportedExtensions(const std::string& extension = std::string(), us::ModuleContext* context = us::GetModuleContext()); std::string GetSupportedWriters(const std::string& basedataType, us::ModuleContext* context = us::GetModuleContext()); protected: - std::vector< us::ServiceReference > GetWriterList(const std::string& extension, us::ModuleContext* context); - std::vector< us::ServiceReference > GetWriterListByBasedataType(const std::string& basedataType, us::ModuleContext* context); - std::string CreateFileDialogString(const std::vector >& refs); bool WriterSupportsOptions(mitk::IFileWriter* writer, const mitk::IFileWriter::OptionNames& options); private: // purposely not implemented FileWriterRegistry(const FileWriterRegistry&); FileWriterRegistry& operator=(const FileWriterRegistry&); std::map > m_ServiceObjects; }; } // namespace mitk #ifdef _MSC_VER # pragma warning(pop) #endif #endif /* FileWriterRegistry_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Core/Code/Testing/mitkFileWriterManagerTest.cpp b/Core/Code/Testing/mitkFileWriterManagerTest.cpp index e2e951169d..9758d1ed17 100644 --- a/Core/Code/Testing/mitkFileWriterManagerTest.cpp +++ b/Core/Code/Testing/mitkFileWriterManagerTest.cpp @@ -1,230 +1,230 @@ /*=================================================================== 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 #include #include #include #include #include #include #include #include #include #include #include class DummyWriter : public mitk::AbstractFileWriter { public: DummyWriter(const DummyWriter& other) : mitk::AbstractFileWriter(other) { } DummyWriter(const std::string& basedataType, const std::string& extension, int priority) : mitk::AbstractFileWriter(basedataType, extension, "This is a dummy description") { m_Priority = priority; m_ServiceReg = this->RegisterService(); } ~DummyWriter() { if (m_ServiceReg) m_ServiceReg.Unregister(); } using AbstractFileWriter::Write; virtual void Write(const mitk::BaseData* /*data*/, std::ostream& /*stream*/ ) { } virtual void SetOptions(const std::list< std::string >& options ) { m_Options = options; //m_Registration.SetProperties(ConstructServiceProperties()); } private: DummyWriter* Clone() const { return new DummyWriter(*this); } us::ServiceRegistration m_ServiceReg; }; // End of internal dummy Writer class DummyWriter2 : public mitk::AbstractFileWriter { public: DummyWriter2(const DummyWriter2& other) : mitk::AbstractFileWriter(other) { } DummyWriter2(const std::string& basedataType, const std::string& extension, int priority) : mitk::AbstractFileWriter(basedataType, extension, "This is a dummy description") { m_Priority = priority; m_ServiceReg = this->RegisterService(); } ~DummyWriter2() { if (m_ServiceReg) m_ServiceReg.Unregister(); } using AbstractFileWriter::Write; virtual void Write(const mitk::BaseData* /*data*/, std::ostream& /*stream*/ ) { } virtual void SetOptions(const std::list< std::string >& options ) { m_Options = options; //m_Registration.SetProperties(ConstructServiceProperties()); } private: DummyWriter2* Clone() const { return new DummyWriter2(*this); } us::ServiceRegistration m_ServiceReg; }; // End of internal dummy Writer 2 /** * TODO */ int mitkFileWriterManagerTest(int argc , char* argv[]) { // always start with this! MITK_TEST_BEGIN("FileWriterManager"); // mitk::FileWriterRegistry::Pointer frm = mitk::FileWriterRegistry::New(); // MITK_TEST_CONDITION_REQUIRED(argc == 2,"Testing FileWriterRegistry instantiation"); DummyWriter testDR("testdata", "test", 1); DummyWriter otherDR("testdata", "other", 1); // MITK_TEST_CONDITION_REQUIRED(testDR->CanWrite("/this/is/a/folder/file.test"),"Positive test of default CanRead() implementation"); // MITK_TEST_CONDITION_REQUIRED(!testDR->CanWrite("/this/is/a/folder/file.tes"),"Negative test of default CanRead() implementation"); mitk::FileWriterRegistry* writerManager = new mitk::FileWriterRegistry; - mitk::IFileWriter* returned = writerManager->GetWriter("test"); + mitk::IFileWriter* returned = writerManager->GetWriter("", "test"); MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(testDR) != returned,"Testing correct retrieval of FileWriter 1/2"); - returned = writerManager->GetWriter("other"); + returned = writerManager->GetWriter("", "other"); MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(otherDR) != returned,"Testing correct retrieval of FileWriter 2/2"); DummyWriter mediocreTestDR("testdata", "test", 20); DummyWriter prettyFlyTestDR("testdata", "test", 50); DummyWriter2 awesomeTestDR("testdata", "test", 100); returned = writerManager->GetWriter("test"); MITK_TEST_CONDITION_REQUIRED(dynamic_cast(&awesomeTestDR), "Testing correct priorized retrieval of FileWriter: Best Writer"); // Now to give those Writers some options, then we will try again mitk::IFileWriter::OptionList options; options.push_back(std::make_pair("isANiceGuy", true)); mediocreTestDR.SetOptions(options); options.clear(); options.push_back(std::make_pair("canFly", true)); prettyFlyTestDR.SetOptions(options); options.push_back(std::make_pair("isAwesome", true)); awesomeTestDR.SetOptions(options); //note: awesomeWriter canFly and isAwesome // Reset Options, use to define what we want the Writer to do mitk::IFileWriter::OptionNames optionFilter; optionFilter.push_back("canFly"); - returned = writerManager->GetWriter("test", options); + returned = writerManager->GetWriter("", "test", optionFilter); MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(awesomeTestDR) != returned, "Testing correct retrieval of FileWriter with Options: Best Writer with options"); optionFilter.push_back("isAwesome"); - returned = writerManager->GetWriter("test", options); + returned = writerManager->GetWriter("", "test", optionFilter); MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(awesomeTestDR) != returned, "Testing correct retrieval of FileWriter with multiple Options: Best Writer with options"); optionFilter.clear(); optionFilter.push_back("isANiceGuy"); - returned = writerManager->GetWriter("test", options); + returned = writerManager->GetWriter("", "test", optionFilter); MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(mediocreTestDR) != returned, "Testing correct retrieval of specific FileWriter with Options: Low priority Writer with specific option"); optionFilter.push_back("canFly"); - returned = writerManager->GetWriter("test", options); + returned = writerManager->GetWriter("", "test", optionFilter); MITK_TEST_CONDITION_REQUIRED(returned == NULL, "Testing correct return of 0 value when no matching Writer was found"); // Onward to test the retrieval of multiple Writers std::vector< mitk::IFileWriter* > returnedList; - returnedList = writerManager->GetWriters("test", options); + returnedList = writerManager->GetWriters("", "test", optionFilter); MITK_TEST_CONDITION_REQUIRED(returnedList.empty(), "Testing correct return of zero Writers when no matching Writer was found, asking for all compatibles"); optionFilter.clear(); optionFilter.push_back("canFly"); - returnedList = writerManager->GetWriters("test", options); + returnedList = writerManager->GetWriters("", "test", optionFilter); MITK_TEST_CONDITION_REQUIRED(returnedList.size() == 2, "Testing correct return of two Writers when two matching Writer was found, asking for all compatibles"); MITK_TEST_CONDITION_REQUIRED(dynamic_cast(returnedList.front()), "Testing correct priorization of returned Writers with options 1/2"); optionFilter.clear(); optionFilter.push_back("isAwesome"); - returnedList = writerManager->GetWriters("test", options); + returnedList = writerManager->GetWriters("", "test", optionFilter); MITK_TEST_CONDITION_REQUIRED(returnedList.size() == 1, "Testing correct return of one Writers when one matching Writer was found, asking for all compatibles"); MITK_TEST_CONDITION_REQUIRED(dynamic_cast(returnedList.front()), "Testing correctness of result from former query"); mitk::CoreObjectFactory::GetInstance(); //mitk::FileReaderManager readerManager; //mitk::Image::Pointer image = readerManager.Read("F://Build//MITK-Data//Pic2DplusT.nrrd"); //writerManager->Write(image.GetPointer(), "F://Build//MITK-Data//Pic2DplusTcopy.nrrd"); //// And now to verify a working read chain for a mps file: //mitk::PointSetWriter::Pointer psr = mitk::PointSetWriter::New(); //mitk::BaseData::Pointer basedata; //basedata = mitk::FileWriterRegistry::Read("F://Build//MITK-Data//pointSet.mps"); //MITK_TEST_CONDITION_REQUIRED(basedata.IsNotNull(), "Testing correct read of PointSet"); //// Testing templated call to WriterManager //mitk::PointSet::Pointer pointset = mitk::FileWriterRegistry::Read< mitk::PointSet >("F://Build//MITK-Data//pointSet.mps"); //MITK_TEST_CONDITION_REQUIRED(pointset.IsNotNull(), "Testing templated call of Read()"); //// And now for something completely different... (Debug) //mitk::LegacyFileWriterService::Pointer lfr = mitk::LegacyFileWriterService::New(".nrrd", "Nearly Raw Raster Data"); //returned = mitk::FileWriterRegistry::GetWriter(".nrrd"); //MITK_TEST_CONDITION_REQUIRED(lfr == returned, "Testing correct retrieval of specific FileWriter with Options: Low priority Writer with specific option"); //mitk::BaseData::Pointer image = mitk::FileWriterRegistry::Read("F://Build//MITK-Data//Pic2DplusT.nrrd"); //MITK_TEST_CONDITION_REQUIRED(image.IsNotNull(), "Testing whether BaseData is empty or not"); //mitk::Image::Pointer image2 = dynamic_cast (image.GetPointer()); //MITK_TEST_CONDITION_REQUIRED(image2.IsNotNull(), "Testing if BaseData is an image"); // Delete this here because it will call the PrototypeServiceFactory::Unget() method // of the dummy writers. delete writerManager; //// always end with this! MITK_TEST_END(); }