diff --git a/Core/Code/Common/mitkException.cpp b/Core/Code/Common/mitkException.cpp index 26e39bb838..f374adcd9c 100644 --- a/Core/Code/Common/mitkException.cpp +++ b/Core/Code/Common/mitkException.cpp @@ -1,17 +1,43 @@ /*=================================================================== 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 "mitkException.h" + +void mitk::Exception::AddRethrowData(const char *file, unsigned int lineNumber, const char *message) + { + mitk::Exception::ReThrowData data = {file, lineNumber, message}; + this->m_RethrowData.push_back(data); + } + +int mitk::Exception::GetNumberOfRethrows() + { + return (int)m_RethrowData.size(); + } + +void mitk::Exception::GetRethrowData(int rethrowNumber, std::string &file, int &line, std::string &message) + { + if ((rethrowNumber >= m_RethrowData.size()) || (rethrowNumber<0)) + { + file = ""; + line = 0; + message = ""; + return; + } + + file = m_RethrowData.at(rethrowNumber).RethrowClassname; + line = m_RethrowData.at(rethrowNumber).RethrowLine; + message = m_RethrowData.at(rethrowNumber).RethrowMessage; + } diff --git a/Core/Code/Common/mitkException.h b/Core/Code/Common/mitkException.h index 56ceb03923..206eecdcdc 100644 --- a/Core/Code/Common/mitkException.h +++ b/Core/Code/Common/mitkException.h @@ -1,88 +1,113 @@ /*=================================================================== 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 MITKEXCEPTION_H_INCLUDED #define MITKEXCEPTION_H_INCLUDED #include #include +#include namespace mitk { /**Documentation * \brief An object of this class represents an exception of MITK. * Please don't instantiate exceptions manually, but use the * exception macros (file mitkExceptionMacro.h) instead. * Simple use in your code is: * * mitkThrow() << "optional exception message"; * * You can also define specialized exceptions which must inherit * from this class. Please always use the mitkExceptionClassMacro * when implementing specialized exceptions. A simple implementation * can look like: * * class MyException : public mitk::Exception * { * public: * mitkExceptionClassMacro(MyException,mitk::Exception); * }; * * You can then throw your specialized exceptions by using the macro * * mitkThrowException(MyException) << "optional exception message"; */ class MITK_CORE_EXPORT Exception : public itk::ExceptionObject { public: Exception(const char *file, unsigned int lineNumber=0, const char *desc="None", const char *loc="Unknown") : itk::ExceptionObject(file,lineNumber,desc,loc){} virtual ~Exception() throw() {} itkTypeMacro(ClassName, SuperClassName); - + + /** \brief Adds rethrow data to this exception. */ + void AddRethrowData(const char *file, unsigned int lineNumber, const char *message); + + /** \return Returns how often the exception was rethrown. */ + int GetNumberOfRethrows(); + + /** @return Returns the rethrow data of the specified rethrow number. Returns empty data, if the rethrowNumber doesn't exist. + * @param rethrowNumber The internal number of the rethrow. + * @param file (returnvalue) This varaiable will be filled with the file of the specified rethrow. + * @param file (returnvalue) This varaiable will be filled with the line of the specified rethrow. + * @param file (returnvalue) This varaiable will be filled with the message of the specified rethrow. + */ + void GetRethrowData(int rethrowNumber, std::string &file, int &line, std::string &message); + /** \brief Definition of the bit shift operator for this class.*/ template inline Exception& operator<<(const T& data) { std::stringstream ss; ss << this->GetDescription() << data; this->SetDescription(ss.str()); return *this; } /** \brief Definition of the bit shift operator for this class (for non const data).*/ template inline Exception& operator<<(T& data) { std::stringstream ss; ss << this->GetDescription() << data; this->SetDescription(ss.str()); return *this; } /** \brief Definition of the bit shift operator for this class (for functions).*/ inline Exception& operator<<(std::ostream& (*func)(std::ostream&)) { std::stringstream ss; ss << this->GetDescription() << func; this->SetDescription(ss.str()); return *this; } + protected: + + struct ReThrowData + { + std::string RethrowClassname; + int RethrowLine; + std::string RethrowMessage; + }; + + std::vector m_RethrowData; }; } // namespace mitk #endif diff --git a/Core/Code/Common/mitkExceptionMacro.h b/Core/Code/Common/mitkExceptionMacro.h index cad5321ffa..c3949776dc 100644 --- a/Core/Code/Common/mitkExceptionMacro.h +++ b/Core/Code/Common/mitkExceptionMacro.h @@ -1,76 +1,96 @@ /*=================================================================== 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 MITK_EXCEPTIONMACRO_H_DEFINED #define MITK_EXCEPTIONMACRO_H_DEFINED #include #include #include #include "mitkException.h" -/** The exception macro is used to print error information / throw an exception +/** The exception macro is used to throw an exception * (i.e., usually a condition that results in program failure). * * Example usage looks like: * mitkThrow() << "this is error info"; */ #define mitkThrow() throw mitk::Exception(__FILE__,__LINE__,"",ITK_LOCATION) -/** The specialized exception macro is used to print error information / throw exceptions +/** The rethrow macro is used to rethrow an existing exception. The + * rethrow information (file,line of code) is then additionally stored + * in the exception. To check if an exception was rethrown you can use + * the methods GetNumberOfRethrows() and GetRethrowData(). + * + * Example usage: + * try + * { + * //some code that throws an exception + * } + * catch(mitk::Exception e) + * { + * //here we want to rethrow the exception + * mitkmitkReThrow(e) << "Message that will be appended to the exception (optional)"; + * } + */ +#define mitkReThrow(mitkexception) \ + mitkexception.AddRethrowData(__FILE__,__LINE__,"Rethrow by mitkReThrow macro.");\ + throw mitkexception + +/** The specialized exception macro is used to throw exceptions * in cases of specialized errors. This means the second parameter must be a class which * inherits from mitk::Exception. An object of this exception is thrown when using the macro. * Thus, more differentiated excaptions can be thrown, when needed. * * Example usage: * mitkSpecializedExceptionMacro(mitk::MySpecializedException) << "this is error info"; */ #define mitkThrowException(classname) throw classname(__FILE__,__LINE__,"",ITK_LOCATION) /** Class macro for MITK exception classes. * All MITK exception classes should derive from MITK::Exception. */ #define mitkExceptionClassMacro(ClassName,SuperClassName) \ ClassName(const char *file, unsigned int lineNumber, const char *desc, const char *loc) :\ SuperClassName(file,lineNumber,desc,loc){}\ itkTypeMacro(ClassName, SuperClassName);\ /** \brief Definition of the bit shift operator for this class. It can be used to add messages.*/\ template inline ClassName& operator<<(const T& data)\ {\ std::stringstream ss;\ ss << this->GetDescription() << data;\ this->SetDescription(ss.str());\ return *this;\ }\ /** \brief Definition of the bit shift operator for this class (for non const data).*/\ template inline ClassName& operator<<(T& data)\ {\ std::stringstream ss;\ ss << this->GetDescription() << data;\ this->SetDescription(ss.str());\ return *this;\ }\ /** \brief Definition of the bit shift operator for this class (for functions).*/\ inline ClassName& operator<<(std::ostream& (*func)(std::ostream&))\ {\ std::stringstream ss;\ ss << this->GetDescription() << func;\ this->SetDescription(ss.str());\ return *this;\ }\ #endif diff --git a/Core/Code/Testing/mitkExceptionTest.cpp b/Core/Code/Testing/mitkExceptionTest.cpp index 825595a898..6884599f43 100644 --- a/Core/Code/Testing/mitkExceptionTest.cpp +++ b/Core/Code/Testing/mitkExceptionTest.cpp @@ -1,222 +1,323 @@ /*=================================================================== 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 "mitkExceptionMacro.h" #include "mitkTestingMacros.h" #include #include #include class SpecializedTestException : public mitk::Exception { public: mitkExceptionClassMacro(SpecializedTestException,mitk::Exception); }; class ExceptionTestClass : public itk::Object { public: mitkClassMacro( ExceptionTestClass , itk::Object ); itkNewMacro(Self); void throwExceptionManually() //this method is ONLY to test the constructor and no code example //normally exceptions should only be thrown by using the exception macro! { throw mitk::Exception("test.cpp",155,"",""); } void throwSpecializedExceptionManually() //this method is ONLY to test the constructor and no code example //normally exceptions should only be thrown by using the exception macro! { throw SpecializedTestException("test.cpp",155,"",""); } void throwExceptionManually(std::string message1, std::string message2) //this method is ONLY to test methods of mitk::Exception and no code example //normally exceptions should only be thrown by using the exception macro! { throw mitk::Exception("testfile.cpp",155,message1.c_str(),"") << message2; } void throwExceptionWithThrowMacro() { mitkThrow()<<"TEST EXCEPION THROWING WITH mitkThrow()"; } void throwExceptionWithThrowMacro(std::string message) { mitkThrow()<throwExceptionManually(); } catch(mitk::Exception) { exceptionThrown = true; } MITK_TEST_CONDITION_REQUIRED(exceptionThrown,"Testing constructor of mitkException"); exceptionThrown = false; try { myExceptionTestObject->throwSpecializedExceptionManually(); } catch(SpecializedTestException) { exceptionThrown = true; } MITK_TEST_CONDITION_REQUIRED(exceptionThrown,"Testing constructor specialized exception (deriving from mitkException)"); } static void TestExceptionMessageStream() { //##### this method is ONLY to test the streaming operators of the exceptions and //##### NO code example. Please do not instantiate exceptions by yourself in normal code! //##### Normally exceptions should only be thrown by using the exception macro! mitk::Exception myException = mitk::Exception("testfile.cpp",111,"testmessage"); myException << " and additional stream"; MITK_TEST_CONDITION_REQUIRED(myException.GetDescription() == std::string("testmessage and additional stream"),"Testing mitkException message stream (adding std::string)"); myException.SetDescription("testmessage2"); myException << ' ' << 'a' << 'n' << 'd' << ' ' << 'c' << 'h' << 'a' << 'r' << 's'; MITK_TEST_CONDITION_REQUIRED(myException.GetDescription() == std::string("testmessage2 and chars"),"Testing mitkException message stream (adding single chars)"); myException.SetDescription("testmessage3"); myException << myException; //adding the object itself makes no sense but should work MITK_TEST_CONDITION_REQUIRED(myException.GetDescription() != std::string(""),"Testing mitkException message stream (adding object)"); SpecializedTestException mySpecializedException = SpecializedTestException("testfile.cpp",111,"testmessage","test"); mySpecializedException << " and additional stream"; MITK_TEST_CONDITION_REQUIRED(mySpecializedException.GetDescription() == std::string("testmessage and additional stream"),"Testing specialized exception message stream (adding std::string)"); } static void TestExceptionMessageStreamThrowing() { bool exceptionThrown = false; ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); std::string thrownMessage = ""; try { myExceptionTestObject->throwExceptionManually("message1"," and message2"); } catch(mitk::Exception e) { thrownMessage = e.GetDescription(); exceptionThrown = true; } MITK_TEST_CONDITION_REQUIRED(exceptionThrown && (thrownMessage == std::string("message1 and message2")),"Testing throwing and streaming of mitk::Exception together.") } static void TestMitkThrowMacro() { bool exceptionThrown = false; ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); //case 1: test throwing try { myExceptionTestObject->throwExceptionWithThrowMacro(); } catch(mitk::Exception) { exceptionThrown = true; } MITK_TEST_CONDITION_REQUIRED(exceptionThrown,"Testing mitkThrow()"); //case 2: test message text exceptionThrown = false; std::string messageText = ""; try { myExceptionTestObject->throwExceptionWithThrowMacro("test123"); } catch(mitk::Exception e) { exceptionThrown = true; messageText = e.GetDescription(); } MITK_TEST_CONDITION_REQUIRED((exceptionThrown && (messageText=="test123")),"Testing message test of mitkThrow()"); //case 3: specialized exception / command mitkThrow(mitk::Exception) exceptionThrown = false; messageText = ""; try { myExceptionTestObject->throwSpecializedExceptionWithThrowMacro("test123"); } catch(mitk::Exception e) { exceptionThrown = true; messageText = e.GetDescription(); } MITK_TEST_CONDITION_REQUIRED(exceptionThrown && messageText=="test123","Testing special exception with mitkThrow(mitk::Exception)"); //case 4: specialized exception / command mitkThrow(mitk::SpecializedException) exceptionThrown = false; messageText = ""; try { myExceptionTestObject->throwSpecializedExceptionWithThrowMacro2("test123"); } catch(SpecializedTestException e) { exceptionThrown = true; messageText = e.GetDescription(); } MITK_TEST_CONDITION_REQUIRED(exceptionThrown && messageText=="test123","Testing special exception with mitkThrow(mitk::SpecializedException)"); + } + +static void TestRethrowInformation() + //this method is ONLY to test methods of mitk::Exception and no code example + //normally exceptions should only be instantiated and thrown by using the exception macros! + { + //first: testing rethrow information methods, when no information is stored + + //case 1.1: method GetNumberOfRethrows() + mitk::Exception e = mitk::Exception("test.cpp",155,"",""); + MITK_TEST_CONDITION_REQUIRED(e.GetNumberOfRethrows()==0,"Testing GetNumberOfRethrows() with empty rethrow information"); + + //case 1.2: GetRethrowData() with negative number + { + std::string file = "invalid"; + int line = -1; + std::string message = "invalid"; + e.GetRethrowData(-1,file,line,message); + MITK_TEST_CONDITION_REQUIRED(((file == "")&&(line==0)&&(message == "")),"Testing GetRethrowData() with invalid rethrow number (negative)."); + } + + //case 1.3: GetRethrowData() with number 0 + { + std::string file = "invalid"; + int line= -1; + std::string message = "invalid"; + e.GetRethrowData(0,file,line,message); + MITK_TEST_CONDITION_REQUIRED(((file == "")&&(line==0)&&(message == "")),"Testing GetRethrowData() with non-existing rethrow number (0)."); + } + + //case 1.4: GetRethrowData() with number 1 + { + std::string file = "invalid"; + int line= -1; + std::string message = "invalid"; + e.GetRethrowData(1,file,line,message); + MITK_TEST_CONDITION_REQUIRED(((file == "")&&(line==0)&&(message == "")),"Testing GetRethrowData() with non-existing rethrow number (1)."); + } + + + //second: add rethrow data + e.AddRethrowData("test2.cpp",10,"Rethrow one"); + MITK_TEST_CONDITION_REQUIRED(e.GetNumberOfRethrows()==1,"Testing adding of rethrow data."); + e.AddRethrowData("test3.cpp",15,"Rethrow two"); + MITK_TEST_CONDITION_REQUIRED(e.GetNumberOfRethrows()==2,"Testing adding of more rethrow data."); + + //third: test if this rethrow data was stored properly + { + std::string file = "invalid"; + int line= -1; + std::string message = "invalid"; + e.GetRethrowData(0,file,line,message); + MITK_TEST_CONDITION_REQUIRED(((file == "test2.cpp")&&(line==10)&&(message == "Rethrow one")),"Testing stored information of first rethrow."); + } + + { + std::string file = "invalid"; + int line= -1; + std::string message = "invalid"; + e.GetRethrowData(1,file,line,message); + MITK_TEST_CONDITION_REQUIRED(((file == "test3.cpp")&&(line==15)&&(message == "Rethrow two")),"Testing stored information of second rethrow."); + } + + + } + +static void TestRethrowMacro() + { + bool exceptionThrown = false; + std::string message = ""; + ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); + + //case 1: test throwing + + try + { + myExceptionTestObject->reThrowExceptionWithReThrowMacro("Test original message.","Test rethrow message."); + } + catch(mitk::Exception e) + { + message = e.GetDescription(); + exceptionThrown = true; + } + MITK_TEST_CONDITION_REQUIRED(exceptionThrown,"Testing mitkReThrow()"); + MITK_TEST_CONDITION_REQUIRED(message == "Test original message.Test rethrow message.", "Testing message/descriprion after rethrow.") + } }; int mitkExceptionTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("MITKException"); ExceptionTestClass::TestExceptionConstructor(); ExceptionTestClass::TestExceptionMessageStream(); ExceptionTestClass::TestExceptionMessageStreamThrowing(); ExceptionTestClass::TestMitkThrowMacro(); + ExceptionTestClass::TestRethrowInformation(); + ExceptionTestClass::TestRethrowMacro(); MITK_TEST_END(); }