From cbf693fa62cd51f4ca5c881838bbb609edc447b0 Mon Sep 17 00:00:00 2001 From: guigues Date: Mon, 9 Feb 2009 10:09:24 +0000 Subject: [PATCH] Starting version 2 --- CMakeLists.txt | 26 +- appli/CMakeLists.txt | 10 +- appli/gimmick/CMakeLists.txt | 11 + appli/gimmick/main.cxx | 9 + src/CMakeLists.txt | 1 - src/creaImageIODicomNode.cpp | 2 +- src/creaImageIODicomNodeComparators.cpp | 4 +- src/creaImageIOWxGimmick.cpp | 11 + src/creaImageIOWxGimmick.h | 46 +- src2/AdditionalUsecreaImageIO2.cmake.in | 2 + src2/AdditionalcreaImageIO2Config.cmake.in | 2 + src2/CMakeLists.txt | 137 ++ src2/CppSQLite3.cpp | 1500 +++++++++++++++++++ src2/CppSQLite3.h | 309 ++++ src2/README.txt | 71 + src2/creaImageIOGimmick.cpp | 158 ++ src2/creaImageIOGimmick.h | 50 + src2/creaImageIOImageFinder.cpp | 288 ++++ src2/creaImageIOImageFinder.h | 91 ++ src2/creaImageIOImageReader.cpp | 505 +++++++ src2/creaImageIOImageReader.h | 70 + src2/creaImageIOIndexedHeap.h | 139 ++ src2/creaImageIOMultiThreadImageReader.cpp | 590 ++++++++ src2/creaImageIOMultiThreadImageReader.h | 246 +++ src2/creaImageIOSQLiteTreeHandler.cpp | 979 ++++++++++++ src2/creaImageIOSQLiteTreeHandler.h | 190 +++ src2/creaImageIOSystem.h | 19 + src2/creaImageIOTree.cpp | 22 + src2/creaImageIOTree.h | 78 + src2/creaImageIOTreeAttributeDescriptor.cpp | 43 + src2/creaImageIOTreeAttributeDescriptor.h | 77 + src2/creaImageIOTreeComparators.cpp | 1 + src2/creaImageIOTreeComparators.h | 212 +++ src2/creaImageIOTreeDescriptor.cpp | 152 ++ src2/creaImageIOTreeDescriptor.h | 74 + src2/creaImageIOTreeHandler.h | 114 ++ src2/creaImageIOTreeLevelDescriptor.cpp | 10 + src2/creaImageIOTreeLevelDescriptor.h | 52 + src2/creaImageIOTreeNode.cpp | 101 ++ src2/creaImageIOTreeNode.h | 137 ++ 40 files changed, 6524 insertions(+), 15 deletions(-) create mode 100644 appli/gimmick/CMakeLists.txt create mode 100644 appli/gimmick/main.cxx create mode 100644 src2/AdditionalUsecreaImageIO2.cmake.in create mode 100644 src2/AdditionalcreaImageIO2Config.cmake.in create mode 100644 src2/CMakeLists.txt create mode 100644 src2/CppSQLite3.cpp create mode 100644 src2/CppSQLite3.h create mode 100644 src2/README.txt create mode 100644 src2/creaImageIOGimmick.cpp create mode 100644 src2/creaImageIOGimmick.h create mode 100644 src2/creaImageIOImageFinder.cpp create mode 100644 src2/creaImageIOImageFinder.h create mode 100644 src2/creaImageIOImageReader.cpp create mode 100644 src2/creaImageIOImageReader.h create mode 100644 src2/creaImageIOIndexedHeap.h create mode 100644 src2/creaImageIOMultiThreadImageReader.cpp create mode 100644 src2/creaImageIOMultiThreadImageReader.h create mode 100644 src2/creaImageIOSQLiteTreeHandler.cpp create mode 100644 src2/creaImageIOSQLiteTreeHandler.h create mode 100644 src2/creaImageIOSystem.h create mode 100644 src2/creaImageIOTree.cpp create mode 100644 src2/creaImageIOTree.h create mode 100644 src2/creaImageIOTreeAttributeDescriptor.cpp create mode 100644 src2/creaImageIOTreeAttributeDescriptor.h create mode 100644 src2/creaImageIOTreeComparators.cpp create mode 100644 src2/creaImageIOTreeComparators.h create mode 100644 src2/creaImageIOTreeDescriptor.cpp create mode 100644 src2/creaImageIOTreeDescriptor.h create mode 100644 src2/creaImageIOTreeHandler.h create mode 100644 src2/creaImageIOTreeLevelDescriptor.cpp create mode 100644 src2/creaImageIOTreeLevelDescriptor.h create mode 100644 src2/creaImageIOTreeNode.cpp create mode 100644 src2/creaImageIOTreeNode.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 221a8e4..8a97a2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,18 +30,30 @@ MARK_AS_ADVANCED( LIBRARY_OUTPUT_PATH ) -# Includes -INCLUDE_DIRECTORIES( -# ${PROJECT_BINARY_DIR} - ${PROJECT_SOURCE_DIR}/src - ) - IF(WIN32) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/win32) LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/win32) ENDIF(WIN32) -SUBDIRS(src) + +OPTION(BUILD_V2 "Build Version 2 ?" OFF) + +IF (BUILD_V2) + INCLUDE_DIRECTORIES( + ${PROJECT_BINARY_DIR} + ${PROJECT_SOURCE_DIR}/src2 + ) + SUBDIRS(src2) +ELSE (BUILD_V2) + INCLUDE_DIRECTORIES( + ${PROJECT_BINARY_DIR} + ${PROJECT_SOURCE_DIR}/src + ) + SUBDIRS(src) +ENDIF (BUILD_V2) + + +# SUBDIRS(appli) SUBDIRS(bbtk) SUBDIRS(install) diff --git a/appli/CMakeLists.txt b/appli/CMakeLists.txt index 95cdfde..cdd44c7 100644 --- a/appli/CMakeLists.txt +++ b/appli/CMakeLists.txt @@ -1 +1,9 @@ -SUBDIRS(TestWxGimmickDialog) +IF (BUILD_V2) + SUBDIRS(gimmick) +ELSE (BUILD_V2) + SUBDIRS(TestWxGimmickDialog) +ENDIF (BUILD_V2) + + + + diff --git a/appli/gimmick/CMakeLists.txt b/appli/gimmick/CMakeLists.txt new file mode 100644 index 0000000..20b6c9d --- /dev/null +++ b/appli/gimmick/CMakeLists.txt @@ -0,0 +1,11 @@ + +IF(WIN32) + ADD_EXECUTABLE(gimmick main) + SET_TARGET_PROPERTIES(gimmick PROPERTIES LINK_FLAGS /subsystem:console ) +ELSE(WIN32) + ADD_EXECUTABLE(gimmick main) +ENDIF(WIN32) + +TARGET_LINK_LIBRARIES( gimmick creaImageIO2) + +INSTALL_TARGETS(/bin/ gimmick ) diff --git a/appli/gimmick/main.cxx b/appli/gimmick/main.cxx new file mode 100644 index 0000000..7f21a16 --- /dev/null +++ b/appli/gimmick/main.cxx @@ -0,0 +1,9 @@ +#include + +int main(int argc, char* argv[]) +{ + creaImageIO::Gimmick g; + if (!g.Initialize()) return 1; + if (!g.Finalize()) return 1; + return 0; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1aff598..1344659 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,6 @@ SET( SRCS creaImageIOWxGimmickDialog - # creaImageIOWxDicomDatabaseTreeView # creaImageIOWxDicomDatabaseTreeViewSettings # creaImageIOWxDicomNodeFieldsView diff --git a/src/creaImageIODicomNode.cpp b/src/creaImageIODicomNode.cpp index 1ddbd6f..04879c8 100644 --- a/src/creaImageIODicomNode.cpp +++ b/src/creaImageIODicomNode.cpp @@ -189,7 +189,7 @@ namespace creaImageIO //============================================================= void DicomNode::Print() const { - for (int i=0;i "<GetFieldValue(mKey) == y->GetFieldValue(mKey) ) return false; - float ix = atof(x->GetFieldValue(mKey).c_str()); - float iy = atof(y->GetFieldValue(mKey).c_str()); + float ix = (float)atof(x->GetFieldValue(mKey).c_str()); + float iy = (float)atof(y->GetFieldValue(mKey).c_str()); return (ix < iy); } //=================================================================== diff --git a/src/creaImageIOWxGimmick.cpp b/src/creaImageIOWxGimmick.cpp index ef0439d..74bebb7 100644 --- a/src/creaImageIOWxGimmick.cpp +++ b/src/creaImageIOWxGimmick.cpp @@ -63,6 +63,8 @@ namespace creaImageIO PopUp_Settings = 501, PopUp_About = 502, PopUp_User = WxGimmick::UserMenuFirstId, + PopUp_SaveAs = 701, + PopUp_AddToFavorites = 702 }; //================================================================ @@ -931,11 +933,13 @@ namespace creaImageIO { wxBusyCursor busy; // std::cout << "WxGimmick : Reading config"< Loading collections from '"<GetSelectionSize(); } + /// Returns true if there is a valid selection bool IsSelectionValid(); + /// Returns the vector of full filenames of selected images void GetSelectedFiles(std::vector&); + /// Returns the vector of images corresponding to selection void GetSelectedImages(std::vector&); + /// Returns the vector of DicomNode corresponding to selection void GetSelectedDicomNodes(std::vector&); + /// Returns the vector of wxTreeItemId corresponding to selection void GetSelectedItems(std::vector&); + + /// Returns the DicomNode corresponding to the tree item DicomNode* GetDicomNodeOfItem(const wxTreeItemId& i); + /// Stores the first id of user menu static const int UserMenuFirstId; + /// The class storing WxGimmick settings typedef WxGimmickSettings Settings; + /// Returns the settings (const) const Settings& GetSettings() const { return mSettings; } + /// Returns the settings Settings& GetSettings() { return mSettings; } + + /// The type of event sent by WxGimmick typedef WxGimmickEvent EventType; + /// The type of list of DicomDatabase typedef std::vector DicomDatabaseListType; + /// Returns the list of DicomDatabase open DicomDatabaseListType& GetDicomDatabaseList() { return mDicomDatabaseList; } + /// Returns the list of DicomDatabase open (const) const DicomDatabaseListType& GetDicomDatabaseList() const { return mDicomDatabaseList; } + /// Returns the wxTreeListCtrl of the main view wxTreeListCtrl* GetTreeListCtrl() { return mTreeListCtrl; } + /// Returns the wxTreeListCtrl of the main view (const) const wxTreeListCtrl* GetTreeListCtrl() const { return mTreeListCtrl; } + /// void SetConfigurationFile(const std::string& filename) { mConfigurationFile = filename;} void LoadConfiguration(); void SaveConfiguration(); void SetSaveConfigurationOnClose(bool v) { mSaveConfigurationOnClose = v; } - + void LoadOrCreateFavoritesDatabase(); protected: + + /// Completely rebuilds the view (i.e. the wxTreeListCtrl) with + /// current DicomDatabaseList void RebuildView(); + /// Recursively updates the part of the view corresponding + /// to the DicomDatabase passed + /// i.e. creates items for the DicomNode which do not have + /// deletes obsolete items (whose DicomNode has been deleted) void UpdateDicomDatabaseView(DicomDatabase*); + /// Recursively updates the part of the view corresponding + /// to the DicomNode provided. + /// parent is its parent in the tree (where to insert / remove it) void UpdateDicomNodeView(DicomNode* n, const wxTreeItemId& parent); + /// Recursively deletes void DeleteObsoleteChildren(wxTreeItemId& id); + /// Create the column titles of the children of the item wxTreeItemId CreateChildrenColumnsTitles(wxTreeItemId& item, DicomNode::Type t); + /// void UpdateColumnsTitles(wxTreeItemId& item, DicomNode::Type t); + /// void UpdateColumns(wxTreeItemId& item, bool only_first = false); + + + + /// General entry point for void OpenOrNewDatabase(bool open); void InsertDicomDatabase(wxTreeItemId& id, DicomDatabase* r); @@ -118,6 +156,7 @@ namespace creaImageIO int mCurrentSelectionImageSize[4]; DicomDatabaseListType mDicomDatabaseList; + DicomDatabase* mFavoriteDatabase; wxTreeListCtrl* mTreeListCtrl; wxTreeItemId mTreeRootId; @@ -239,6 +278,7 @@ namespace creaImageIO void OnPopUpAddFile(wxCommandEvent& event); void OnPopUpAddRawFile(wxCommandEvent& event); void OnPopUpAddDirectory(wxCommandEvent& event); + void OnPopUpAddToFavorites(wxCommandEvent& event); void OnPopUpRemove(wxCommandEvent& event); void OnPopUpSettings(wxCommandEvent& event); void OnPopUpAbout(wxCommandEvent& event); diff --git a/src2/AdditionalUsecreaImageIO2.cmake.in b/src2/AdditionalUsecreaImageIO2.cmake.in new file mode 100644 index 0000000..bdf20d7 --- /dev/null +++ b/src2/AdditionalUsecreaImageIO2.cmake.in @@ -0,0 +1,2 @@ +INCLUDE(${crea_USE_FILE}) + diff --git a/src2/AdditionalcreaImageIO2Config.cmake.in b/src2/AdditionalcreaImageIO2Config.cmake.in new file mode 100644 index 0000000..35ae10d --- /dev/null +++ b/src2/AdditionalcreaImageIO2Config.cmake.in @@ -0,0 +1,2 @@ +# We have to find crea +FIND_PACKAGE(crea REQUIRED) \ No newline at end of file diff --git a/src2/CMakeLists.txt b/src2/CMakeLists.txt new file mode 100644 index 0000000..41e2ed0 --- /dev/null +++ b/src2/CMakeLists.txt @@ -0,0 +1,137 @@ +SET(LIBRARY_NAME creaImageIO2) + +SET( SRCS + # SQLite + CppSQLite3 + + # wxTreeListCtrl +# treelistctrl.cpp + + + # Attributed tree data structure + creaImageIOTree + creaImageIOTreeNode + creaImageIOTreeDescriptor + creaImageIOTreeLevelDescriptor + creaImageIOTreeAttributeDescriptor + creaImageIOTreeComparators + + # Image readers + creaImageIOImageReader + creaImageIOMultiThreadImageReader + + # Tree Handlers + creaImageIOTreeHandler + creaImageIOSQLiteTreeHandler + + # + creaImageIOImageFinder + + # + creaImageIOGimmick +# creaImageIODicomNode +# creaImageIODicomNodeComparators +# creaImageIODicomNodeTypeDescription +# creaImageIODicomDatabaseStructure +# creaImageIODicomDatabase +# creaImageIOField + + # The Gimmick! widgets +# creaImageIOWxGimmick +# creaImageIOWxGimmickSettings +# creaImageIOWxGimmickFieldsView + +# creaImageIOWxGimmickDialog + +) + + + +OPTION(${LIBRARY_NAME}_BUILD_SHARED + "Build ${LIBRARY_NAME} as a shared library (dynamic) ?" ON) +IF (${LIBRARY_NAME}_BUILD_SHARED) + SET(CREAIMAGEIO_BUILD_SHARED SHARED) + crea_DEFINE(CREAIMAGEIO_BUILD_SHARED) +ENDIF(${LIBRARY_NAME}_BUILD_SHARED) + +crea_DEFINE(CREAIMAGEIO_EXPORT_SYMBOLS) + +ADD_LIBRARY(${LIBRARY_NAME} ${CREAIMAGEIO_BUILD_SHARED} ${SRCS}) + + + +TARGET_LINK_LIBRARIES(${LIBRARY_NAME} + ${crea_LIBRARIES} + ${WXWIDGETS_LIBRARIES} + ${VTK_LIBRARIES} + ${GDCM_LIBRARIES} + ${BOOST_LIBRARIES} + sqlite3) + +#---------------------------------------------------------------------------- +# INSTALLS LIBRARY +FILE(GLOB HEADERS "*.h") +INSTALL( + FILES ${HEADERS} + DESTINATION include/${LIBRARY_NAME} + ) +IF (WIN32) + SET(CREAIMAGEIO_LIB_PATH bin) +ELSE (WIN32) + SET(CREAIMAGEIO_LIB_PATH lib) +ENDIF(WIN32) + +INSTALL( + TARGETS ${LIBRARY_NAME} + DESTINATION ${CREAIMAGEIO_LIB_PATH}) + + + # Sets the settings for macro CREA_ADVANCED_INSTALL_LIBRARY_FOR_CMAKE + SET(${LIBRARY_NAME}_INSTALL_FOLDER ${LIBRARY_NAME}) + SET(${LIBRARY_NAME}_LIBRARIES ${LIBRARY_NAME}) + + FILE(RELATIVE_PATH + ${LIBRARY_NAME}_BUILD_TREE_RELATIVE_INCLUDE_PATHS + ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} + ) + IF ( ${PROJECT_BINARY_DIR} STREQUAL ${EXECUTABLE_OUTPUT_PATH} ) + SET(CILFC_EXECUTABLE_OUTPUT_REL_PATH ".") + ELSE ( ${PROJECT_BINARY_DIR} STREQUAL ${EXECUTABLE_OUTPUT_PATH} ) + FILE(RELATIVE_PATH + CILFC_EXECUTABLE_OUTPUT_REL_PATH + ${PROJECT_BINARY_DIR} ${EXECUTABLE_OUTPUT_PATH}) + ENDIF ( ${PROJECT_BINARY_DIR} STREQUAL ${EXECUTABLE_OUTPUT_PATH} ) + + IF(UNIX) + SET(${LIBRARY_NAME}_BUILD_TREE_RELATIVE_LIBRARY_PATHS + ${CILFC_EXECUTABLE_OUTPUT_REL_PATH}) + SET(${LIBRARY_NAME}_INSTALL_TREE_RELATIVE_LIBRARY_PATHS lib) + ELSE(UNIX) + SET(${LIBRARY_NAME}_BUILD_TREE_RELATIVE_LIBRARY_PATHS + ${CILFC_EXECUTABLE_OUTPUT_REL_PATH}) + SET(${LIBRARY_NAME}_INSTALL_TREE_RELATIVE_LIBRARY_PATHS bin) + ENDIF(UNIX) + SET(${LIBRARY_NAME}_INSTALL_TREE_RELATIVE_INCLUDE_PATHS include/${LIBRARY_NAME}) + + SET(${LIBRARY_NAME}_HAS_ADDITIONAL_CONFIG_FILE TRUE) +SET(${LIBRARY_NAME}_ADDITIONAL_CONFIG_FILE + ${PROJECT_SOURCE_DIR}/src2/Additional${LIBRARY_NAME}Config.cmake.in) +SET(${LIBRARY_NAME}_ADDITIONAL_USE_FILE + ${PROJECT_SOURCE_DIR}/src2/AdditionalUse${LIBRARY_NAME}.cmake.in) + + # Invoke the advanced macro + CREA_ADVANCED_INSTALL_LIBRARY_FOR_CMAKE(${LIBRARY_NAME}) + + +#CREA_INSTALL_LIBRARY_FOR_CMAKE(${LIBRARY_NAME}) +#----------------------------------------------------------------------------- + +OPTION( BUILD_DOXYGEN_DOC "Build doxygen doc ?" OFF) +IF(BUILD_DOXYGEN_DOC) + SUBDIRS(doxygen) +ENDIF(BUILD_DOXYGEN_DOC) + +INCLUDE_DIRECTORIES( +# ${PROJECT_BINARY_DIR} + ${PROJECT_SOURCE_DIR}/src2 + ) diff --git a/src2/CppSQLite3.cpp b/src2/CppSQLite3.cpp new file mode 100644 index 0000000..3d074d5 --- /dev/null +++ b/src2/CppSQLite3.cpp @@ -0,0 +1,1500 @@ +//////////////////////////////////////////////////////////////////////////////// +// CppSQLite3 - A C++ wrapper around the SQLite3 embedded database library. +// +// Copyright (c) 2004 Rob Groves. All Rights Reserved. rob.groves@btinternet.com +// +// Permission to use, copy, modify, and distribute this software and its +// documentation for any purpose, without fee, and without a written +// agreement, is hereby granted, provided that the above copyright notice, +// this paragraph and the following two paragraphs appear in all copies, +// modifications, and distributions. +// +// IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, +// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST +// PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, +// EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF +// ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". THE AUTHOR HAS NO OBLIGATION +// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// +// V3.0 03/08/2004 -Initial Version for sqlite3 +// +// V3.1 16/09/2004 -Implemented getXXXXField using sqlite3 functions +// -Added CppSQLiteDB3::tableExists() +//////////////////////////////////////////////////////////////////////////////// +#include "CppSQLite3.h" +#include + + +// Named constant for passing to CppSQLite3Exception when passing it a string +// that cannot be deleted. +static const bool DONT_DELETE_MSG=false; + +//////////////////////////////////////////////////////////////////////////////// +// Prototypes for SQLite functions not included in SQLite DLL, but copied below +// from SQLite encode.c +//////////////////////////////////////////////////////////////////////////////// +int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out); +int sqlite3_decode_binary(const unsigned char *in, unsigned char *out); + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// + +CppSQLite3Exception::CppSQLite3Exception(const int nErrCode, + const char* szErrMess, + bool bDeleteMsg/*=true*/) : + mnErrCode(nErrCode) +{ + mpszErrMess = sqlite3_mprintf("%s[%d]: %s", + errorCodeAsString(nErrCode), + nErrCode, + szErrMess ? szErrMess : ""); + /* + if (bDeleteMsg && szErrMess) + { + sqlite3_free(szErrMess); + } + */ +} + +CppSQLite3Exception::CppSQLite3Exception(const int nErrCode, + char* szErrMess, + bool bDeleteMsg/*=true*/) : + mnErrCode(nErrCode) +{ + mpszErrMess = sqlite3_mprintf("%s[%d]: %s", + errorCodeAsString(nErrCode), + nErrCode, + szErrMess ? szErrMess : ""); + + if (bDeleteMsg && szErrMess) + { + sqlite3_free(szErrMess); + } +} + +CppSQLite3Exception::CppSQLite3Exception(const CppSQLite3Exception& e) : + mnErrCode(e.mnErrCode) +{ + mpszErrMess = 0; + if (e.mpszErrMess) + { + mpszErrMess = sqlite3_mprintf("%s", e.mpszErrMess); + } +} + + +const char* CppSQLite3Exception::errorCodeAsString(int nErrCode) +{ + switch (nErrCode) + { + case SQLITE_OK : return "SQLITE_OK"; + case SQLITE_ERROR : return "SQLITE_ERROR"; + case SQLITE_INTERNAL : return "SQLITE_INTERNAL"; + case SQLITE_PERM : return "SQLITE_PERM"; + case SQLITE_ABORT : return "SQLITE_ABORT"; + case SQLITE_BUSY : return "SQLITE_BUSY"; + case SQLITE_LOCKED : return "SQLITE_LOCKED"; + case SQLITE_NOMEM : return "SQLITE_NOMEM"; + case SQLITE_READONLY : return "SQLITE_READONLY"; + case SQLITE_INTERRUPT : return "SQLITE_INTERRUPT"; + case SQLITE_IOERR : return "SQLITE_IOERR"; + case SQLITE_CORRUPT : return "SQLITE_CORRUPT"; + case SQLITE_NOTFOUND : return "SQLITE_NOTFOUND"; + case SQLITE_FULL : return "SQLITE_FULL"; + case SQLITE_CANTOPEN : return "SQLITE_CANTOPEN"; + case SQLITE_PROTOCOL : return "SQLITE_PROTOCOL"; + case SQLITE_EMPTY : return "SQLITE_EMPTY"; + case SQLITE_SCHEMA : return "SQLITE_SCHEMA"; + case SQLITE_TOOBIG : return "SQLITE_TOOBIG"; + case SQLITE_CONSTRAINT : return "SQLITE_CONSTRAINT"; + case SQLITE_MISMATCH : return "SQLITE_MISMATCH"; + case SQLITE_MISUSE : return "SQLITE_MISUSE"; + case SQLITE_NOLFS : return "SQLITE_NOLFS"; + case SQLITE_AUTH : return "SQLITE_AUTH"; + case SQLITE_FORMAT : return "SQLITE_FORMAT"; + case SQLITE_RANGE : return "SQLITE_RANGE"; + case SQLITE_ROW : return "SQLITE_ROW"; + case SQLITE_DONE : return "SQLITE_DONE"; + case CPPSQLITE_ERROR : return "CPPSQLITE_ERROR"; + default: return "UNKNOWN_ERROR"; + } +} + + +CppSQLite3Exception::~CppSQLite3Exception() +{ + if (mpszErrMess) + { + sqlite3_free(mpszErrMess); + mpszErrMess = 0; + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +CppSQLite3Buffer::CppSQLite3Buffer() +{ + mpBuf = 0; +} + + +CppSQLite3Buffer::~CppSQLite3Buffer() +{ + clear(); +} + + +void CppSQLite3Buffer::clear() +{ + if (mpBuf) + { + sqlite3_free(mpBuf); + mpBuf = 0; + } + +} + + +const char* CppSQLite3Buffer::format(const char* szFormat, ...) +{ + clear(); + va_list va; + va_start(va, szFormat); + mpBuf = sqlite3_vmprintf(szFormat, va); + va_end(va); + return mpBuf; +} + + +//////////////////////////////////////////////////////////////////////////////// + +CppSQLite3Binary::CppSQLite3Binary() : + mpBuf(0), + mnBinaryLen(0), + mnBufferLen(0), + mnEncodedLen(0), + mbEncoded(false) +{ +} + + +CppSQLite3Binary::~CppSQLite3Binary() +{ + clear(); +} + + +void CppSQLite3Binary::setBinary(const unsigned char* pBuf, int nLen) +{ + mpBuf = allocBuffer(nLen); + memcpy(mpBuf, pBuf, nLen); +} + + +void CppSQLite3Binary::setEncoded(const unsigned char* pBuf) +{ + clear(); + + mnEncodedLen = strlen((const char*)pBuf); + mnBufferLen = mnEncodedLen + 1; // Allow for NULL terminator + + mpBuf = (unsigned char*)malloc(mnBufferLen); + + if (!mpBuf) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Cannot allocate memory", + DONT_DELETE_MSG); + } + + memcpy(mpBuf, pBuf, mnBufferLen); + mbEncoded = true; +} + + +const unsigned char* CppSQLite3Binary::getEncoded() +{ + if (!mbEncoded) + { + unsigned char* ptmp = (unsigned char*)malloc(mnBinaryLen); + memcpy(ptmp, mpBuf, mnBinaryLen); + mnEncodedLen = sqlite3_encode_binary(ptmp, mnBinaryLen, mpBuf); + free(ptmp); + mbEncoded = true; + } + + return mpBuf; +} + + +const unsigned char* CppSQLite3Binary::getBinary() +{ + if (mbEncoded) + { + // in/out buffers can be the same + mnBinaryLen = sqlite3_decode_binary(mpBuf, mpBuf); + + if (mnBinaryLen == -1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Cannot decode binary", + DONT_DELETE_MSG); + } + + mbEncoded = false; + } + + return mpBuf; +} + + +int CppSQLite3Binary::getBinaryLength() +{ + getBinary(); + return mnBinaryLen; +} + + +unsigned char* CppSQLite3Binary::allocBuffer(int nLen) +{ + clear(); + + // Allow extra space for encoded binary as per comments in + // SQLite encode.c See bottom of this file for implementation + // of SQLite functions use 3 instead of 2 just to be sure ;-) + mnBinaryLen = nLen; + mnBufferLen = 3 + (257*nLen)/254; + + mpBuf = (unsigned char*)malloc(mnBufferLen); + + if (!mpBuf) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Cannot allocate memory", + DONT_DELETE_MSG); + } + + mbEncoded = false; + + return mpBuf; +} + + +void CppSQLite3Binary::clear() +{ + if (mpBuf) + { + mnBinaryLen = 0; + mnBufferLen = 0; + free(mpBuf); + mpBuf = 0; + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +CppSQLite3Query::CppSQLite3Query() +{ + mpVM = 0; + mbEof = true; + mnCols = 0; + mbOwnVM = false; +} + + +CppSQLite3Query::CppSQLite3Query(const CppSQLite3Query& rQuery) +{ + mpVM = rQuery.mpVM; + // Only one object can own the VM + const_cast(rQuery).mpVM = 0; + mbEof = rQuery.mbEof; + mnCols = rQuery.mnCols; + mbOwnVM = rQuery.mbOwnVM; +} + + +CppSQLite3Query::CppSQLite3Query(sqlite3* pDB, + sqlite3_stmt* pVM, + bool bEof, + bool bOwnVM/*=true*/) +{ + mpDB = pDB; + mpVM = pVM; + mbEof = bEof; + mnCols = sqlite3_column_count(mpVM); + mbOwnVM = bOwnVM; +} + + +CppSQLite3Query::~CppSQLite3Query() +{ + try + { + finalize(); + } + catch (...) + { + } +} + + +CppSQLite3Query& CppSQLite3Query::operator=(const CppSQLite3Query& rQuery) +{ + try + { + finalize(); + } + catch (...) + { + } + mpVM = rQuery.mpVM; + // Only one object can own the VM + const_cast(rQuery).mpVM = 0; + mbEof = rQuery.mbEof; + mnCols = rQuery.mnCols; + mbOwnVM = rQuery.mbOwnVM; + return *this; +} + + +int CppSQLite3Query::numFields() +{ + checkVM(); + return mnCols; +} + + +const char* CppSQLite3Query::fieldValue(int nField) +{ + checkVM(); + + if (nField < 0 || nField > mnCols-1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid field index requested", + DONT_DELETE_MSG); + } + + return (const char*)sqlite3_column_text(mpVM, nField); +} + + +const char* CppSQLite3Query::fieldValue(const char* szField) +{ + int nField = fieldIndex(szField); + return (const char*)sqlite3_column_text(mpVM, nField); +} + + +int CppSQLite3Query::getIntField(int nField, int nNullValue/*=0*/) +{ + if (fieldDataType(nField) == SQLITE_NULL) + { + return nNullValue; + } + else + { + return sqlite3_column_int(mpVM, nField); + } +} + + +int CppSQLite3Query::getIntField(const char* szField, int nNullValue/*=0*/) +{ + int nField = fieldIndex(szField); + return getIntField(nField, nNullValue); +} + + +double CppSQLite3Query::getFloatField(int nField, double fNullValue/*=0.0*/) +{ + if (fieldDataType(nField) == SQLITE_NULL) + { + return fNullValue; + } + else + { + return sqlite3_column_double(mpVM, nField); + } +} + + +double CppSQLite3Query::getFloatField(const char* szField, double fNullValue/*=0.0*/) +{ + int nField = fieldIndex(szField); + return getFloatField(nField, fNullValue); +} + + +const char* CppSQLite3Query::getStringField(int nField, const char* szNullValue/*=""*/) +{ + if (fieldDataType(nField) == SQLITE_NULL) + { + return szNullValue; + } + else + { + return (const char*)sqlite3_column_text(mpVM, nField); + } +} + + +const char* CppSQLite3Query::getStringField(const char* szField, const char* szNullValue/*=""*/) +{ + int nField = fieldIndex(szField); + return getStringField(nField, szNullValue); +} + + +const unsigned char* CppSQLite3Query::getBlobField(int nField, int& nLen) +{ + checkVM(); + + if (nField < 0 || nField > mnCols-1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid field index requested", + DONT_DELETE_MSG); + } + + nLen = sqlite3_column_bytes(mpVM, nField); + return (const unsigned char*)sqlite3_column_blob(mpVM, nField); +} + + +const unsigned char* CppSQLite3Query::getBlobField(const char* szField, int& nLen) +{ + int nField = fieldIndex(szField); + return getBlobField(nField, nLen); +} + + +bool CppSQLite3Query::fieldIsNull(int nField) +{ + return (fieldDataType(nField) == SQLITE_NULL); +} + + +bool CppSQLite3Query::fieldIsNull(const char* szField) +{ + int nField = fieldIndex(szField); + return (fieldDataType(nField) == SQLITE_NULL); +} + + +int CppSQLite3Query::fieldIndex(const char* szField) +{ + checkVM(); + + if (szField) + { + for (int nField = 0; nField < mnCols; nField++) + { + const char* szTemp = sqlite3_column_name(mpVM, nField); + + if (strcmp(szField, szTemp) == 0) + { + return nField; + } + } + } + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid field name requested", + DONT_DELETE_MSG); +} + + +const char* CppSQLite3Query::fieldName(int nCol) +{ + checkVM(); + + if (nCol < 0 || nCol > mnCols-1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid field index requested", + DONT_DELETE_MSG); + } + + return sqlite3_column_name(mpVM, nCol); +} + + +const char* CppSQLite3Query::fieldDeclType(int nCol) +{ + checkVM(); + + if (nCol < 0 || nCol > mnCols-1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid field index requested", + DONT_DELETE_MSG); + } + + return sqlite3_column_decltype(mpVM, nCol); +} + + +int CppSQLite3Query::fieldDataType(int nCol) +{ + checkVM(); + + if (nCol < 0 || nCol > mnCols-1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid field index requested", + DONT_DELETE_MSG); + } + + return sqlite3_column_type(mpVM, nCol); +} + + +bool CppSQLite3Query::eof() +{ + checkVM(); + return mbEof; +} + + +void CppSQLite3Query::nextRow() +{ + checkVM(); + + int nRet = sqlite3_step(mpVM); + + if (nRet == SQLITE_DONE) + { + // no rows + mbEof = true; + } + else if (nRet == SQLITE_ROW) + { + // more rows, nothing to do + } + else + { + nRet = sqlite3_finalize(mpVM); + mpVM = 0; + const char* szError = sqlite3_errmsg(mpDB); + throw CppSQLite3Exception(nRet, + (char*)szError, + DONT_DELETE_MSG); + } +} + + +void CppSQLite3Query::finalize() +{ + if (mpVM && mbOwnVM) + { + int nRet = sqlite3_finalize(mpVM); + mpVM = 0; + if (nRet != SQLITE_OK) + { + const char* szError = sqlite3_errmsg(mpDB); + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + } + } +} + + +void CppSQLite3Query::checkVM() +{ + if (mpVM == 0) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Null Virtual Machine pointer", + DONT_DELETE_MSG); + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +CppSQLite3Table::CppSQLite3Table() +{ + mpaszResults = 0; + mnRows = 0; + mnCols = 0; + mnCurrentRow = 0; +} + + +CppSQLite3Table::CppSQLite3Table(const CppSQLite3Table& rTable) +{ + mpaszResults = rTable.mpaszResults; + // Only one object can own the results + const_cast(rTable).mpaszResults = 0; + mnRows = rTable.mnRows; + mnCols = rTable.mnCols; + mnCurrentRow = rTable.mnCurrentRow; +} + + +CppSQLite3Table::CppSQLite3Table(char** paszResults, int nRows, int nCols) +{ + mpaszResults = paszResults; + mnRows = nRows; + mnCols = nCols; + mnCurrentRow = 0; +} + + +CppSQLite3Table::~CppSQLite3Table() +{ + try + { + finalize(); + } + catch (...) + { + } +} + + +CppSQLite3Table& CppSQLite3Table::operator=(const CppSQLite3Table& rTable) +{ + try + { + finalize(); + } + catch (...) + { + } + mpaszResults = rTable.mpaszResults; + // Only one object can own the results + const_cast(rTable).mpaszResults = 0; + mnRows = rTable.mnRows; + mnCols = rTable.mnCols; + mnCurrentRow = rTable.mnCurrentRow; + return *this; +} + + +void CppSQLite3Table::finalize() +{ + if (mpaszResults) + { + sqlite3_free_table(mpaszResults); + mpaszResults = 0; + } +} + + +int CppSQLite3Table::numFields() +{ + checkResults(); + return mnCols; +} + + +int CppSQLite3Table::numRows() +{ + checkResults(); + return mnRows; +} + + +const char* CppSQLite3Table::fieldValue(int nField) +{ + checkResults(); + + if (nField < 0 || nField > mnCols-1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid field index requested", + DONT_DELETE_MSG); + } + + int nIndex = (mnCurrentRow*mnCols) + mnCols + nField; + return mpaszResults[nIndex]; +} + + +const char* CppSQLite3Table::fieldValue(const char* szField) +{ + checkResults(); + + if (szField) + { + for (int nField = 0; nField < mnCols; nField++) + { + if (strcmp(szField, mpaszResults[nField]) == 0) + { + int nIndex = (mnCurrentRow*mnCols) + mnCols + nField; + return mpaszResults[nIndex]; + } + } + } + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid field name requested", + DONT_DELETE_MSG); +} + + +int CppSQLite3Table::getIntField(int nField, int nNullValue/*=0*/) +{ + if (fieldIsNull(nField)) + { + return nNullValue; + } + else + { + return atoi(fieldValue(nField)); + } +} + + +int CppSQLite3Table::getIntField(const char* szField, int nNullValue/*=0*/) +{ + if (fieldIsNull(szField)) + { + return nNullValue; + } + else + { + return atoi(fieldValue(szField)); + } +} + + +double CppSQLite3Table::getFloatField(int nField, double fNullValue/*=0.0*/) +{ + if (fieldIsNull(nField)) + { + return fNullValue; + } + else + { + return atof(fieldValue(nField)); + } +} + + +double CppSQLite3Table::getFloatField(const char* szField, double fNullValue/*=0.0*/) +{ + if (fieldIsNull(szField)) + { + return fNullValue; + } + else + { + return atof(fieldValue(szField)); + } +} + + +const char* CppSQLite3Table::getStringField(int nField, const char* szNullValue/*=""*/) +{ + if (fieldIsNull(nField)) + { + return szNullValue; + } + else + { + return fieldValue(nField); + } +} + + +const char* CppSQLite3Table::getStringField(const char* szField, const char* szNullValue/*=""*/) +{ + if (fieldIsNull(szField)) + { + return szNullValue; + } + else + { + return fieldValue(szField); + } +} + + +bool CppSQLite3Table::fieldIsNull(int nField) +{ + checkResults(); + return (fieldValue(nField) == 0); +} + + +bool CppSQLite3Table::fieldIsNull(const char* szField) +{ + checkResults(); + return (fieldValue(szField) == 0); +} + + +const char* CppSQLite3Table::fieldName(int nCol) +{ + checkResults(); + + if (nCol < 0 || nCol > mnCols-1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid field index requested", + DONT_DELETE_MSG); + } + + return mpaszResults[nCol]; +} + + +void CppSQLite3Table::setRow(int nRow) +{ + checkResults(); + + if (nRow < 0 || nRow > mnRows-1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid row index requested", + DONT_DELETE_MSG); + } + + mnCurrentRow = nRow; +} + + +void CppSQLite3Table::checkResults() +{ + if (mpaszResults == 0) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Null Results pointer", + DONT_DELETE_MSG); + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +CppSQLite3Statement::CppSQLite3Statement() +{ + mpDB = 0; + mpVM = 0; +} + + +CppSQLite3Statement::CppSQLite3Statement(const CppSQLite3Statement& rStatement) +{ + mpDB = rStatement.mpDB; + mpVM = rStatement.mpVM; + // Only one object can own VM + const_cast(rStatement).mpVM = 0; +} + + +CppSQLite3Statement::CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM) +{ + mpDB = pDB; + mpVM = pVM; +} + + +CppSQLite3Statement::~CppSQLite3Statement() +{ + try + { + finalize(); + } + catch (...) + { + } +} + + +CppSQLite3Statement& CppSQLite3Statement::operator=(const CppSQLite3Statement& rStatement) +{ + mpDB = rStatement.mpDB; + mpVM = rStatement.mpVM; + // Only one object can own VM + const_cast(rStatement).mpVM = 0; + return *this; +} + + +int CppSQLite3Statement::execDML() +{ + checkDB(); + checkVM(); + + const char* szError=0; + + int nRet = sqlite3_step(mpVM); + + if (nRet == SQLITE_DONE) + { + int nRowsChanged = sqlite3_changes(mpDB); + + nRet = sqlite3_reset(mpVM); + + if (nRet != SQLITE_OK) + { + szError = sqlite3_errmsg(mpDB); + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + } + + return nRowsChanged; + } + else + { + nRet = sqlite3_reset(mpVM); + szError = sqlite3_errmsg(mpDB); + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + } +} + + +CppSQLite3Query CppSQLite3Statement::execQuery() +{ + checkDB(); + checkVM(); + + int nRet = sqlite3_step(mpVM); + + if (nRet == SQLITE_DONE) + { + // no rows + return CppSQLite3Query(mpDB, mpVM, true/*eof*/, false); + } + else if (nRet == SQLITE_ROW) + { + // at least 1 row + return CppSQLite3Query(mpDB, mpVM, false/*eof*/, false); + } + else + { + nRet = sqlite3_reset(mpVM); + const char* szError = sqlite3_errmsg(mpDB); + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + } +} + + +void CppSQLite3Statement::bind(int nParam, const char* szValue) +{ + checkVM(); + int nRes = sqlite3_bind_text(mpVM, nParam, szValue, -1, SQLITE_TRANSIENT); + + if (nRes != SQLITE_OK) + { + throw CppSQLite3Exception(nRes, + "Error binding string param", + DONT_DELETE_MSG); + } +} + + +void CppSQLite3Statement::bind(int nParam, const int nValue) +{ + checkVM(); + int nRes = sqlite3_bind_int(mpVM, nParam, nValue); + + if (nRes != SQLITE_OK) + { + throw CppSQLite3Exception(nRes, + "Error binding int param", + DONT_DELETE_MSG); + } +} + + +void CppSQLite3Statement::bind(int nParam, const double dValue) +{ + checkVM(); + int nRes = sqlite3_bind_double(mpVM, nParam, dValue); + + if (nRes != SQLITE_OK) + { + throw CppSQLite3Exception(nRes, + "Error binding double param", + DONT_DELETE_MSG); + } +} + + +void CppSQLite3Statement::bind(int nParam, const unsigned char* blobValue, int nLen) +{ + checkVM(); + int nRes = sqlite3_bind_blob(mpVM, nParam, + (const void*)blobValue, nLen, SQLITE_TRANSIENT); + + if (nRes != SQLITE_OK) + { + throw CppSQLite3Exception(nRes, + "Error binding blob param", + DONT_DELETE_MSG); + } +} + + +void CppSQLite3Statement::bindNull(int nParam) +{ + checkVM(); + int nRes = sqlite3_bind_null(mpVM, nParam); + + if (nRes != SQLITE_OK) + { + throw CppSQLite3Exception(nRes, + "Error binding NULL param", + DONT_DELETE_MSG); + } +} + + +void CppSQLite3Statement::reset() +{ + if (mpVM) + { + int nRet = sqlite3_reset(mpVM); + + if (nRet != SQLITE_OK) + { + const char* szError = sqlite3_errmsg(mpDB); + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + } + } +} + + +void CppSQLite3Statement::finalize() +{ + if (mpVM) + { + int nRet = sqlite3_finalize(mpVM); + mpVM = 0; + + if (nRet != SQLITE_OK) + { + const char* szError = sqlite3_errmsg(mpDB); + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + } + } +} + + +void CppSQLite3Statement::checkDB() +{ + if (mpDB == 0) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Database not open", + DONT_DELETE_MSG); + } +} + + +void CppSQLite3Statement::checkVM() +{ + if (mpVM == 0) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Null Virtual Machine pointer", + DONT_DELETE_MSG); + } +} + + +//////////////////////////////////////////////////////////////////////////////// + +CppSQLite3DB::CppSQLite3DB() +{ + mpDB = 0; + mnBusyTimeoutMs = 60000; // 60 seconds +} + + +CppSQLite3DB::CppSQLite3DB(const CppSQLite3DB& db) +{ + mpDB = db.mpDB; + mnBusyTimeoutMs = 60000; // 60 seconds +} + + +CppSQLite3DB::~CppSQLite3DB() +{ + close(); +} + + +CppSQLite3DB& CppSQLite3DB::operator=(const CppSQLite3DB& db) +{ + mpDB = db.mpDB; + mnBusyTimeoutMs = 60000; // 60 seconds + return *this; +} + + +void CppSQLite3DB::open(const char* szFile) +{ + int nRet = sqlite3_open(szFile, &mpDB); + + if (nRet != SQLITE_OK) + { + const char* szError = sqlite3_errmsg(mpDB); + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + } + + setBusyTimeout(mnBusyTimeoutMs); +} + + +void CppSQLite3DB::close() +{ + if (mpDB) + { + sqlite3_close(mpDB); + mpDB = 0; + } +} + + +CppSQLite3Statement CppSQLite3DB::compileStatement(const char* szSQL) +{ + checkDB(); + + sqlite3_stmt* pVM = compile(szSQL); + return CppSQLite3Statement(mpDB, pVM); +} + + +bool CppSQLite3DB::tableExists(const char* szTable) +{ + char szSQL[128]; + sprintf(szSQL, + "select count(*) from sqlite_master where type='table' and name='%s'", + szTable); + int nRet = execScalar(szSQL); + return (nRet > 0); +} + + +int CppSQLite3DB::execDML(const char* szSQL) +{ + checkDB(); + + char* szError=0; + + int nRet = sqlite3_exec(mpDB, szSQL, 0, 0, &szError); + + if (nRet == SQLITE_OK) + { + return sqlite3_changes(mpDB); + } + else + { + throw CppSQLite3Exception(nRet, szError); + } +} + + +CppSQLite3Query CppSQLite3DB::execQuery(const char* szSQL) +{ + checkDB(); + + sqlite3_stmt* pVM = compile(szSQL); + + int nRet = sqlite3_step(pVM); + + if (nRet == SQLITE_DONE) + { + // no rows + return CppSQLite3Query(mpDB, pVM, true/*eof*/); + } + else if (nRet == SQLITE_ROW) + { + // at least 1 row + return CppSQLite3Query(mpDB, pVM, false/*eof*/); + } + else + { + nRet = sqlite3_finalize(pVM); + const char* szError= sqlite3_errmsg(mpDB); + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + } +} + + +int CppSQLite3DB::execScalar(const char* szSQL) +{ + CppSQLite3Query q = execQuery(szSQL); + + if (q.eof() || q.numFields() < 1) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Invalid scalar query", + DONT_DELETE_MSG); + } + + return atoi(q.fieldValue(0)); +} + + +CppSQLite3Table CppSQLite3DB::getTable(const char* szSQL) +{ + checkDB(); + + char* szError=0; + char** paszResults=0; + int nRet; + int nRows(0); + int nCols(0); + + nRet = sqlite3_get_table(mpDB, szSQL, &paszResults, &nRows, &nCols, &szError); + + if (nRet == SQLITE_OK) + { + return CppSQLite3Table(paszResults, nRows, nCols); + } + else + { + throw CppSQLite3Exception(nRet, szError); + } +} + + +sqlite_int64 CppSQLite3DB::lastRowId() +{ + return sqlite3_last_insert_rowid(mpDB); +} + + +void CppSQLite3DB::setBusyTimeout(int nMillisecs) +{ + mnBusyTimeoutMs = nMillisecs; + sqlite3_busy_timeout(mpDB, mnBusyTimeoutMs); +} + + +void CppSQLite3DB::checkDB() +{ + if (!mpDB) + { + throw CppSQLite3Exception(CPPSQLITE_ERROR, + "Database not open", + DONT_DELETE_MSG); + } +} + + +sqlite3_stmt* CppSQLite3DB::compile(const char* szSQL) +{ + checkDB(); + + char* szError=0; + const char* szTail=0; + sqlite3_stmt* pVM; + + int nRet = sqlite3_prepare(mpDB, szSQL, -1, &pVM, &szTail); + + if (nRet != SQLITE_OK) + { + throw CppSQLite3Exception(nRet, szError); + } + + return pVM; +} + + +//////////////////////////////////////////////////////////////////////////////// +// SQLite encode.c reproduced here, containing implementation notes and source +// for sqlite3_encode_binary() and sqlite3_decode_binary() +//////////////////////////////////////////////////////////////////////////////// + +/* +** 2002 April 25 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains helper routines used to translate binary data into +** a null-terminated string (suitable for use in SQLite) and back again. +** These are convenience routines for use by people who want to store binary +** data in an SQLite database. The code in this file is not used by any other +** part of the SQLite library. +** +** $Id: CppSQLite3.cpp,v 1.1 2009/02/09 10:09:33 guigues Exp $ +*/ + +/* +** How This Encoder Works +** +** The output is allowed to contain any character except 0x27 (') and +** 0x00. This is accomplished by using an escape character to encode +** 0x27 and 0x00 as a two-byte sequence. The escape character is always +** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The +** 0x27 character is encoded as the two byte sequence 0x01 0x03. Finally, +** the escape character itself is encoded as the two-character sequence +** 0x01 0x02. +** +** To summarize, the encoder works by using an escape sequences as follows: +** +** 0x00 -> 0x01 0x01 +** 0x01 -> 0x01 0x02 +** 0x27 -> 0x01 0x03 +** +** If that were all the encoder did, it would work, but in certain cases +** it could double the size of the encoded string. For example, to +** encode a string of 100 0x27 characters would require 100 instances of +** the 0x01 0x03 escape sequence resulting in a 200-character output. +** We would prefer to keep the size of the encoded string smaller than +** this. +** +** To minimize the encoding size, we first add a fixed offset value to each +** byte in the sequence. The addition is modulo 256. (That is to say, if +** the sum of the original character value and the offset exceeds 256, then +** the higher order bits are truncated.) The offset is chosen to minimize +** the number of characters in the string that need to be escaped. For +** example, in the case above where the string was composed of 100 0x27 +** characters, the offset might be 0x01. Each of the 0x27 characters would +** then be converted into an 0x28 character which would not need to be +** escaped at all and so the 100 character input string would be converted +** into just 100 characters of output. Actually 101 characters of output - +** we have to record the offset used as the first byte in the sequence so +** that the string can be decoded. Since the offset value is stored as +** part of the output string and the output string is not allowed to contain +** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27. +** +** Here, then, are the encoding steps: +** +** (1) Choose an offset value and make it the first character of +** output. +** +** (2) Copy each input character into the output buffer, one by +** one, adding the offset value as you copy. +** +** (3) If the value of an input character plus offset is 0x00, replace +** that one character by the two-character sequence 0x01 0x01. +** If the sum is 0x01, replace it with 0x01 0x02. If the sum +** is 0x27, replace it with 0x01 0x03. +** +** (4) Put a 0x00 terminator at the end of the output. +** +** Decoding is obvious: +** +** (5) Copy encoded characters except the first into the decode +** buffer. Set the first encoded character aside for use as +** the offset in step 7 below. +** +** (6) Convert each 0x01 0x01 sequence into a single character 0x00. +** Convert 0x01 0x02 into 0x01. Convert 0x01 0x03 into 0x27. +** +** (7) Subtract the offset value that was the first character of +** the encoded buffer from all characters in the output buffer. +** +** The only tricky part is step (1) - how to compute an offset value to +** minimize the size of the output buffer. This is accomplished by testing +** all offset values and picking the one that results in the fewest number +** of escapes. To do that, we first scan the entire input and count the +** number of occurances of each character value in the input. Suppose +** the number of 0x00 characters is N(0), the number of occurances of 0x01 +** is N(1), and so forth up to the number of occurances of 0xff is N(255). +** An offset of 0 is not allowed so we don't have to test it. The number +** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number +** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth. +** In this way we find the offset that gives the minimum number of escapes, +** and thus minimizes the length of the output string. +*/ + +/* +** Encode a binary buffer "in" of size n bytes so that it contains +** no instances of characters '\'' or '\000'. The output is +** null-terminated and can be used as a string value in an INSERT +** or UPDATE statement. Use sqlite3_decode_binary() to convert the +** string back into its original binary. +** +** The result is written into a preallocated output buffer "out". +** "out" must be able to hold at least 2 +(257*n)/254 bytes. +** In other words, the output will be expanded by as much as 3 +** bytes for every 254 bytes of input plus 2 bytes of fixed overhead. +** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.) +** +** The return value is the number of characters in the encoded +** string, excluding the "\000" terminator. +*/ +int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out){ + int i, j, e, m; + int cnt[256]; + if( n<=0 ){ + out[0] = 'x'; + out[1] = 0; + return 1; + } + memset(cnt, 0, sizeof(cnt)); + for(i=n-1; i>=0; i--){ cnt[in[i]]++; } + m = n; + for(i=1; i<256; i++){ + int sum; + if( i=='\'' ) continue; + sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff]; + if( sum +#include + +#define CPPSQLITE_ERROR 1000 + +class CppSQLite3Exception +{ +public: + + CppSQLite3Exception(const int nErrCode, + char* szErrMess, + bool bDeleteMsg=true); + CppSQLite3Exception(const int nErrCode, + const char* szErrMess, + bool bDeleteMsg=true); + + CppSQLite3Exception(const CppSQLite3Exception& e); + + virtual ~CppSQLite3Exception(); + + const int errorCode() { return mnErrCode; } + + const char* errorMessage() { return mpszErrMess; } + + static const char* errorCodeAsString(int nErrCode); + +private: + + int mnErrCode; + char* mpszErrMess; +}; + + +class CppSQLite3Buffer +{ +public: + + CppSQLite3Buffer(); + + ~CppSQLite3Buffer(); + + const char* format(const char* szFormat, ...); + + operator const char*() { return mpBuf; } + + void clear(); + +private: + + char* mpBuf; +}; + + +class CppSQLite3Binary +{ +public: + + CppSQLite3Binary(); + + ~CppSQLite3Binary(); + + void setBinary(const unsigned char* pBuf, int nLen); + void setEncoded(const unsigned char* pBuf); + + const unsigned char* getEncoded(); + const unsigned char* getBinary(); + + int getBinaryLength(); + + unsigned char* allocBuffer(int nLen); + + void clear(); + +private: + + unsigned char* mpBuf; + int mnBinaryLen; + int mnBufferLen; + int mnEncodedLen; + bool mbEncoded; +}; + + +class CppSQLite3Query +{ +public: + + CppSQLite3Query(); + + CppSQLite3Query(const CppSQLite3Query& rQuery); + + CppSQLite3Query(sqlite3* pDB, + sqlite3_stmt* pVM, + bool bEof, + bool bOwnVM=true); + + CppSQLite3Query& operator=(const CppSQLite3Query& rQuery); + + virtual ~CppSQLite3Query(); + + int numFields(); + + int fieldIndex(const char* szField); + const char* fieldName(int nCol); + + const char* fieldDeclType(int nCol); + int fieldDataType(int nCol); + + const char* fieldValue(int nField); + const char* fieldValue(const char* szField); + + int getIntField(int nField, int nNullValue=0); + int getIntField(const char* szField, int nNullValue=0); + + double getFloatField(int nField, double fNullValue=0.0); + double getFloatField(const char* szField, double fNullValue=0.0); + + const char* getStringField(int nField, const char* szNullValue=""); + const char* getStringField(const char* szField, const char* szNullValue=""); + + const unsigned char* getBlobField(int nField, int& nLen); + const unsigned char* getBlobField(const char* szField, int& nLen); + + bool fieldIsNull(int nField); + bool fieldIsNull(const char* szField); + + bool eof(); + + void nextRow(); + + void finalize(); + +private: + + void checkVM(); + + sqlite3* mpDB; + sqlite3_stmt* mpVM; + bool mbEof; + int mnCols; + bool mbOwnVM; +}; + + +class CppSQLite3Table +{ +public: + + CppSQLite3Table(); + + CppSQLite3Table(const CppSQLite3Table& rTable); + + CppSQLite3Table(char** paszResults, int nRows, int nCols); + + virtual ~CppSQLite3Table(); + + CppSQLite3Table& operator=(const CppSQLite3Table& rTable); + + int numFields(); + + int numRows(); + + const char* fieldName(int nCol); + + const char* fieldValue(int nField); + const char* fieldValue(const char* szField); + + int getIntField(int nField, int nNullValue=0); + int getIntField(const char* szField, int nNullValue=0); + + double getFloatField(int nField, double fNullValue=0.0); + double getFloatField(const char* szField, double fNullValue=0.0); + + const char* getStringField(int nField, const char* szNullValue=""); + const char* getStringField(const char* szField, const char* szNullValue=""); + + bool fieldIsNull(int nField); + bool fieldIsNull(const char* szField); + + void setRow(int nRow); + + void finalize(); + +private: + + void checkResults(); + + int mnCols; + int mnRows; + int mnCurrentRow; + char** mpaszResults; +}; + + +class CppSQLite3Statement +{ +public: + + CppSQLite3Statement(); + + CppSQLite3Statement(const CppSQLite3Statement& rStatement); + + CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM); + + virtual ~CppSQLite3Statement(); + + CppSQLite3Statement& operator=(const CppSQLite3Statement& rStatement); + + int execDML(); + + CppSQLite3Query execQuery(); + + void bind(int nParam, const char* szValue); + void bind(int nParam, const int nValue); + void bind(int nParam, const double dwValue); + void bind(int nParam, const unsigned char* blobValue, int nLen); + void bindNull(int nParam); + + void reset(); + + void finalize(); + +private: + + void checkDB(); + void checkVM(); + + sqlite3* mpDB; + sqlite3_stmt* mpVM; +}; + + +class CppSQLite3DB +{ +public: + + CppSQLite3DB(); + + virtual ~CppSQLite3DB(); + + void open(const char* szFile); + + void close(); + + bool tableExists(const char* szTable); + + int execDML(const char* szSQL); + + CppSQLite3Query execQuery(const char* szSQL); + + int execScalar(const char* szSQL); + + CppSQLite3Table getTable(const char* szSQL); + + CppSQLite3Statement compileStatement(const char* szSQL); + + sqlite_int64 lastRowId(); + + void interrupt() { sqlite3_interrupt(mpDB); } + + void setBusyTimeout(int nMillisecs); + + static const char* SQLiteVersion() { return SQLITE_VERSION; } + +private: + + CppSQLite3DB(const CppSQLite3DB& db); + CppSQLite3DB& operator=(const CppSQLite3DB& db); + + sqlite3_stmt* compile(const char* szSQL); + + void checkDB(); + + sqlite3* mpDB; + int mnBusyTimeoutMs; +}; + +#endif diff --git a/src2/README.txt b/src2/README.txt new file mode 100644 index 0000000..c62bb92 --- /dev/null +++ b/src2/README.txt @@ -0,0 +1,71 @@ +New architecture created on 04/02/09 following a MVC approach + +Elements of the architecture: + +* Data structure: Attributed tree +-------------------------------------- +in namespace creaImageIO::tree +Tree : Attributed tree structure + inherits Node + holds a vector of root TreeNode +TreeData : Abstract class to store user data on a tree +Node : + belong to a Tree, + holds a pointer on parent Node, + holds a vector of children Node +NodeData : Abstract class to store user data on a tree node +Descriptor : Descriptor of the structure a tree (number of levels, descriptors of each level, ...) + + holds a vector of LevelDescriptor +LevelDescriptor : + holds a vector of TreeAttributeDescriptor +AttributeDescriptor : + stores name, dicom group/elem, flags +Comparator: Abstract definition of a comparator of Node + Comparison is done by operator()(Node* const &, Node* const &) +ComparatorWithOrder : Abstract Comparator whose order can be reversed + Concrete comparison is done by method compare(Node* const &, Node* const &) +LexicographicalComparator : A Comparator which stores a vector of Comparators and which performs lexicographical comparison + +IntComparator : Compares the values of a given Attribute of the Nodes which is decoded as an int value +FloatComparator : Compares the values of a given Attribute of the Nodes which is decoded as a float value +StringComparator : Compares the values of a given Attribute of the Nodes which is decoded as a string value + + +* Models : TreeHandler and descendants +-------------------------------------- + +TreeHandler : Abstract class which 'handles' a Tree structure. + Can: + Load the children of a given Node + +SQLiteTreeHandler : Concrete TreeHandler which manages a tree stored in a sqlite database +CppSQLite3.h / CppSQLite3.cpp : C++ interface to sqlite db +... + +ImageFinder : Parses (recursively) a part of a filesystem to look for known images and load their attributes in order to add the images to a Tree (submission via a TreeHandler::AddBranch) + + +* Image handling +---------------- + +creaImageIOImageReader.h/cpp : +SpecificImageReader +ImageReader + +creaImageIOMultiThreadImageReader.h/cpp +creaImageIOIndexedHeap.h/cpp + +* Views : +--------- +TreeView +WxTreeView +WxGimmickView +QTreeView +QGimmickView +... + +* Controller : +-------------- +Gimmick +... diff --git a/src2/creaImageIOGimmick.cpp b/src2/creaImageIOGimmick.cpp new file mode 100644 index 0000000..a317684 --- /dev/null +++ b/src2/creaImageIOGimmick.cpp @@ -0,0 +1,158 @@ +#include + +#include + +#include +#include + +namespace creaImageIO +{ + + //============================================================== + Gimmick::Gimmick() + { + crea::MessageManager::RegisterMessageType("Gimmick!", + "Gimmick",1); + } + //============================================================== + + + + //============================================================== + Gimmick::~Gimmick() + { + + } + //============================================================== + + + //============================================================== + bool Gimmick::Initialize() + { + // Create the UserSettings dir if does not exist + if (!CreateUserSettingsDirectory()) return false; + // Sets the current directory to the home dir + mCurrentDirectory = GetHomeDirectory(); + + // Create local database handler + mLocalDatabase = new SQLiteTreeHandler(GetLocalDatabasePath()); + // Create or open local database + if (! boost::filesystem::exists( GetLocalDatabasePath() ) ) + { + creaMessage("Gimmick!",1, + "[Gimmick!] Local database '"<GetTree().GetDescriptor().CreateDefault(); + + if ( ! mLocalDatabase->Create(true) ) + { + creaMessage("Gimmick!",1, + "[Gimmick!] !! ERROR CREATING '"<Open(true) ) + { + creaMessage("Gimmick!",1, + "[Gimmick!] !! ERROR OPENING '"< + +namespace creaImageIO +{ + + //======================================================================= + /// Central controler of the gimmick application + class Gimmick + { + public: + /// Ctor + Gimmick(); + /// Dtor + ~Gimmick(); + + /// Initialize (read/creates databases, etc.) + bool Initialize(); + + /// Finalize (closes databases, etc.) + bool Finalize(); + + + + const std::string& GetHomeDirectory(); + const std::string& GetUserSettingsDirectory(); + bool CreateUserSettingsDirectory(); + const std::string& GetLocalDatabasePath(); + + private: + SQLiteTreeHandler* mLocalDatabase; + + std::string mCurrentDirectory; + std::string mHomeDirectory; + std::string mUserSettingsDirectory; + std::string mLocalDatabasePath; + + }; + // EO class Gimmick + //======================================================================= + + +} // EO namespace creaImageIO + +// EOF +#endif + + diff --git a/src2/creaImageIOImageFinder.cpp b/src2/creaImageIOImageFinder.cpp new file mode 100644 index 0000000..8a28144 --- /dev/null +++ b/src2/creaImageIOImageFinder.cpp @@ -0,0 +1,288 @@ +#include +#include +#include +#include + +using namespace crea; + +namespace creaImageIO +{ + //==================================================================== + // Ctor + ImageFinder::ImageFinder(TreeHandler* tree) + : mTreeHandler(tree) + { + } + // Dtor + ImageFinder::~ImageFinder() + { + } + //==================================================================== + + //===================================================================== + bool ImageFinder::IsHandledFile( const std::string& filename) + { + return (mReader.CanRead(filename,"")); + } + //===================================================================== + + //===================================================================== + bool ImageFinder::AddFiles( const std::vector& filenames, + wxProgressDialog* progress, + UpdateSummary& summary) + { + for (int swi=0;swi<10;swi++) + { + msw[swi].Start(0); + msw[swi].Pause(); + } + + // Parse directory + wxStopWatch sw; + + + summary.added_images = 0; + unsigned int nbf = filenames.size(); + std::vector::const_iterator i; + for (i=filenames.begin();i!=filenames.end();++i) + { + summary.scanned_files++; + if (IsHandledFile(*i)) + { + summary.handled_images++; + AddFile(*i,summary); + + if (progress) + { + std::string mess("Adding "); + mess += *i; + if (!progress->Update( (int)(summary.added_images*999./nbf), + std2wx(mess))) + { + // Some file was added hence we must return true ! + summary.cancelled_by_user = true; + break; + } + } + } + } + + + sw.Pause(); + msw[0].Pause(); + msw[1].Pause(); + msw[2].Pause(); + + summary.total_time = sw.Time(); + summary.file_scan_time = msw[1].Time(); + summary.update_database_time = msw[2].Time(); + summary.update_structs_time = + summary.total_time - + summary.parse_time - + summary.file_scan_time - + summary.update_database_time; + + return true; + } + //===================================================================== + + //===================================================================== + bool ImageFinder::AddFile( const std::string& filename, + UpdateSummary& summary) + { + + std::map< std::string, std::string> attr; + mTreeHandler->GetTree().GetDescriptor().BuildAttributeMap(attr); + + msw[1].Resume(); + mReader.ReadAttributes(filename,attr); + // mReader.ReadDicomInfo(filename,image); + msw[1].Pause(); + + // image->SetFieldValue("FullFileName",filename); + + int lev = mTreeHandler->AddBranch(attr); + + // TO DO : update the summary according to lev + + return true; + } + //===================================================================== + + //===================================================================== + /** + * \brief Explore a directory with possibility of recursion + * return number of files read + * @param dirpath directory to explore + * @param recursive whether we want recursion or not + */ + void ImageFinder::ParseDirectory( const std::string &dirpath, + std::vector &Filenames, + bool recursive, + wxProgressDialog* progress, + UpdateSummary& summary) + + { + if (progress) + { + std::string mess("Parsing "); + mess += dirpath; + progress->Pulse(std2wx(mess)); + } + + wxStopWatch sw; + sw.Start(0); + + std::string fileName; + std::string dirName = dirpath; + + summary.scanned_dirs++; + + wxDir dir( std2wx(dirpath) ); + + if ( !dir.IsOpened() ) + { + // deal with the error here - wxDir would already log an error message + // explaining the exact reason of the failure + return; + } + + wxString filename; + + bool cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN ); + while ( cont ) + { + if ((progress)&&( sw.Time() >= 250 )) + { + // std::cout << "PULSE"<Pulse()) + { + summary.cancelled_by_user = true; + break; + } + } + + summary.scanned_files++; + wxFileName wxffn(dir.GetName(),filename); + std::string ffn = wx2std(wxffn.GetFullPath()); + // std::cout << ffn << std::endl; + if (mReader.CanRead(ffn,"")) + { + Filenames.push_back( ffn ); + summary.handled_images++; + } + cont = dir.GetNext(&filename); + } + + // Recurse into subdirs + if ( recursive ) + { + cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN ); + while ( cont ) + { + + wxFileName wxffn(dir.GetName(),filename); + std::string ffn = wx2std(wxffn.GetFullPath()); + + // std::cout << "dir="<< ffn<< std::endl; + + ParseDirectory( ffn, + Filenames, + recursive, + progress, + summary); + if (summary.cancelled_by_user) break; + + cont = dir.GetNext(&filename); + } + } + + } + //======================================================================= + + + //===================================================================== + bool ImageFinder::AddDirectory( const std::string& directory, + bool recurse, + wxProgressDialog* progress, + UpdateSummary& summary + ) + { + // std::cout << "** ImageFinder::AddDirectory" + // << " '"<Pulse(); + } + + for (int swi=0;swi<10;swi++) + { + msw[swi].Start(0); + msw[swi].Pause(); + } + + // Parse directory + wxStopWatch sw; + + bool was_canceled_by_user(false); + std::vector filenames; + ParseDirectory( directory, + filenames, + recurse, + progress, + summary); + + if ( summary.cancelled_by_user ) + { + return false; + } + + summary.parse_time = sw.Time(); + + + summary.added_images = 0; + unsigned int nbf = filenames.size(); // , nf = 0; + std::vector::iterator i; + for (i=filenames.begin();i!=filenames.end();++i) + { + AddFile(*i,summary); + + if (progress) + { + std::string mess("Adding "); + mess += *i; + if (!progress->Update( (int)(summary.added_images*999./nbf), + std2wx(mess))) + { + // Some file was added hence we must return true ! + summary.cancelled_by_user = true; + break; + } + } + } + + + sw.Pause(); + msw[0].Pause(); + msw[1].Pause(); + msw[2].Pause(); + + summary.total_time = sw.Time(); + summary.file_scan_time = msw[1].Time(); + summary.update_database_time = msw[2].Time(); + summary.update_structs_time = + summary.total_time - + summary.parse_time - + summary.file_scan_time - + summary.update_database_time; + + return true; + } + //===================================================================== + + + +} diff --git a/src2/creaImageIOImageFinder.h b/src2/creaImageIOImageFinder.h new file mode 100644 index 0000000..8eba703 --- /dev/null +++ b/src2/creaImageIOImageFinder.h @@ -0,0 +1,91 @@ +#ifndef __creaImageIOImageFinder_h_INCLUDED__ +#define __creaImageIOImageFinder_h_INCLUDED__ + +#include +#include +#include +#include + +namespace creaImageIO +{ + + + //======================================================================= + /// Parses (recursively) a part of a filesystem to look for known images and load their attributes in order to add the images to a Tree (submission via a TreeHandler::AddBranch) + class ImageFinder + { + public: + ///==================================================================== + /// Ctor + ImageFinder(TreeHandler* tree); + /// Dtor + ~ImageFinder(); + ///==================================================================== + + struct UpdateSummary + { + int scanned_dirs; + int scanned_files; + int handled_images; + int added_images; + + int added_patients; + int added_studies; + int added_series; + + long parse_time; + long file_scan_time; + long update_structs_time; + long update_database_time; + long total_time; + bool cancelled_by_user; + + UpdateSummary() : + scanned_dirs(0), + scanned_files(0), + handled_images(0), + added_images(0), + added_patients(0), + added_studies(0), + added_series(0), + parse_time(0), + file_scan_time(0), + update_structs_time(0), + update_database_time(0), + total_time(0), + cancelled_by_user(false) + {} + }; + /// + bool IsHandledFile( const std::string& filename); + bool AddFile( const std::string& filename, + UpdateSummary& summary ); + bool AddFiles( const std::vector& filename, + wxProgressDialog* progress, + UpdateSummary& summary); + bool AddDirectory( const std::string& directory, + bool recurse, + wxProgressDialog* progress, + UpdateSummary& summary + ); + + void ParseDirectory( const std::string& directory, + std::vector &Filenames, + bool recurse, + wxProgressDialog* progress, + UpdateSummary& summary); + private: + TreeHandler* mTreeHandler; + ImageReader mReader; + wxStopWatch msw[10]; + + }; + // EO class ImageFinder + //======================================================================= + + +} // EO namespace creaImageIO + +// EOF +#endif + diff --git a/src2/creaImageIOImageReader.cpp b/src2/creaImageIOImageReader.cpp new file mode 100644 index 0000000..2480a47 --- /dev/null +++ b/src2/creaImageIOImageReader.cpp @@ -0,0 +1,505 @@ +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +//#include + +#include +#include + + +#include "boost/filesystem/path.hpp" + +namespace creaImageIO +{ + + //======================================================================== + std::string irclean(const std::string& str) + { + if (str == "GDCM::Unfound") + { + return "----"; + } + if (str[str.size()-1]==' ') + { + return str.substr(0,str.size()-1); + } + if (str[str.size()-1]==0) + { + return str.substr(0,str.size()-1); + } + + return str; + } + //======================================================================== + + void IRSplitString ( const std::string& str, + const std::string& delimiters, + std::vector& tokens) + { + // Skip delimiters at beginning. + std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); + // Find first delimiter. + std::string::size_type pos = str.find_first_of(delimiters, lastPos); + + while (std::string::npos != pos || std::string::npos != lastPos) + { + // Found a token, add it to the vector. + // SPECIFIC : REMOVE INITIAL DOT (lastPos + 1) + tokens.push_back(str.substr(lastPos+1, pos - lastPos)); + // Skip delimiters. Note the "not_of" + lastPos = str.find_first_not_of(delimiters, pos); + // Find next delimiter + pos = str.find_first_of(delimiters, lastPos); + } + + } + + + //===================================================================== + class SpecificImageReader + { + public: + SpecificImageReader() {} + virtual ~SpecificImageReader() {} + + void SetName(const std::string& s) { mName = s; } + const std::string& GetName() const { return mName; } + + virtual void PushBackExtensions(std::vector&) {} + + virtual bool CanRead(const std::string& filename) { return false; } + virtual vtkImageData* ReadImage(const std::string& filename) { return 0; } + virtual void ReadAttributes(const std::string& filename, + std::map& attr) {} + + private: + std::string mName; + }; + //===================================================================== + + //===================================================================== + class SpecificVtkReader : public SpecificImageReader + { + public: + //===================================================================== + SpecificVtkReader(vtkImageReader2* r, + const std::string& name = "", + const std::string& extensions = "") + : mVTKReader(r), mExtensions(extensions) + { + if (name.size() == 0) + { + SetName ( mVTKReader->GetDescriptiveName() ); + } + else + { + SetName ( name ); + } + }; + //===================================================================== + ~SpecificVtkReader() + { + mVTKReader->Delete(); + } + //===================================================================== + bool CanRead(const std::string& filename) + { + // std::cout << "## Reader "<CanReadFile(filename.c_str())!=0); + } + //===================================================================== + + //===================================================================== + vtkImageData* ReadImage(const std::string& filename) + { + // std::cout << "## Reader "<SetFileName(filename.c_str()); + mVTKReader->Update(); + im = vtkImageData::New(); + im->ShallowCopy(mVTKReader->GetOutput()); + } + catch (...) + { + if (im!=0) im->Delete(); + im = 0; + } + return im; + } + //===================================================================== + + //===================================================================== + void PushBackExtensions(std::vector& v) + { + std::string ext = mExtensions; + if (ext.size()==0) ext = mVTKReader->GetFileExtensions (); + + IRSplitString(ext," ",v); + } + //===================================================================== + + //===================================================================== + void ReadAttributes(const std::string& filename, + std::map& attr) + { + // std::cout << "SpecificVtkReader::ReadDicomInfo '"<SetFileName(filename.c_str()); + mVTKReader->Update(); //OpenFile(); + int ext[6]; + mVTKReader->GetDataExtent(ext); + // Columns + char cols[128]; + sprintf(cols,"%i",ext[1]-ext[0]); + // Rows + char rows[128]; + sprintf(rows,"%i",ext[3]-ext[2]); + // Planes + char planes[128]; + sprintf(planes,"%i",ext[5]-ext[4]); + + // + std::map::iterator i; + if ( (i = attr.find("Full File Name")) != attr.end()) + { + boost::filesystem::path full_path(filename); + std::string f = full_path.leaf(); + i->second = f; + } + if ( (i = attr.find("Columns")) != attr.end()) + { + i->second = cols; + } + if ( (i = attr.find("Rows")) != attr.end()) + { + i->second = rows; + } + if ( (i = attr.find("Planes")) != attr.end()) + { + i->second = planes; + } + } + //===================================================================== + private: + vtkImageReader2* mVTKReader; + std::string mExtensions; + }; + //===================================================================== + + //===================================================================== + /* + void IRFillFields(DicomNode* node, + GDCM_NAME_SPACE::File* gdcmFile) + { + const DicomNodeTypeDescription::FieldDescriptionMapType& dm + = node->GetTypeDescription().GetFieldDescriptionMap(); + DicomNodeTypeDescription::FieldDescriptionMapType::const_iterator i; + + + DicomNode::FieldValueMapType& vm = node->GetFieldValueMap(); + for (i=dm.begin(); i!=dm.end(); ++i) + { + if ( (i->second.flags==0) && + (i->second.group!=0) && + (i->second.element!=0) ) + { + uint16_t gr = i->second.group; + uint16_t el = i->second.element; + + std::string val = gdcmFile->GetEntryString(gr,el); + + vm[i->first] = irclean(val); + } + else + { + vm[i->first] = ""; + } + } + } + */ + //===================================================================== + + //===================================================================== + class DicomReader : public SpecificImageReader + { + public: + //===================================================================== + DicomReader() + { + mReader = vtkGdcmReader::New(); + SetName ( "Dicom" ); + }; + //===================================================================== + ~DicomReader() + { + mReader->Delete(); + } + //===================================================================== + bool CanRead(const std::string& filename) + { + // std::cout << "## Reader "<SetLoadMode( GDCM_NAME_SPACE::LD_ALL); + file->SetFileName(filename.c_str()); + file->Load(); + bool ok = file->IsReadable(); + file->Delete(); + return ok; + } + //===================================================================== + vtkImageData* ReadImage(const std::string& filename) + { + // std::cout << "## Reader "<SetFileName(filename.c_str()); + mReader->Update(); + im = vtkImageData::New(); + im->ShallowCopy(mReader->GetOutput()); + } + catch (...) + { + if (im!=0) im->Delete(); + im = 0; + } + return im; + } + //===================================================================== + void PushBackExtensions(std::vector& v) + { + v.push_back("dcm"); + v.push_back(""); + } + //===================================================================== + + //===================================================================== + //===================================================================== + void ReadAttributes(const std::string& filename, + std::map& attr) + { + // std::cout << "DicomReader::ReadDicomInfo '"<SetLoadMode( GDCM_NAME_SPACE::LD_ALL); + file->SetFileName(filename.c_str()); + file->Load(); + if (file->IsReadable()) + { + + std::map::iterator i; + for (i=attr.begin();i!=attr.end();++i) + { + if ( i->first == "Full File Name" ) + { + boost::filesystem::path full_path(filename); + std::string f = full_path.leaf(); + i->second = f; + } + else + { + tree::AttributeDescriptor a(i->first); + uint16_t gr = a.GetGroup(); + uint16_t el = a.GetElement(); + if ( ( gr!=0 ) && ( el!=0 ) ) + { + std::string val = file->GetEntryString(gr,el); + i->second = irclean(val); + } + } + } + } + file->Delete(); + } + + + private: + vtkGdcmReader* mReader; + }; + //===================================================================== + + + + + + //===================================================================== + ImageReader::ImageReader() + : + mUnreadableImage(0), + mLastFilename("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"), + mLastReader(0) + + { + // std::cout << "#### ImageReader::ImageReader()"<::const_iterator i; + for (i=GetKnownExtensions().begin(); + i!=GetKnownExtensions().end(); + i++) + { + std::cout << "'"<<(*i)<<"'"<SetDimensions ( dim ); + mUnreadableImage->SetScalarTypeToUnsignedChar(); + mUnreadableImage->AllocateScalars(); + for (int i=0;iSetScalarComponentFromFloat(i,j,0,0,0); + for (int i=0;iSetScalarComponentFromFloat(i,i,0,0,255); + mUnreadableImage->SetScalarComponentFromFloat(dim[0]-1-i,i,0,0,255); + } + + + + } + //===================================================================== + + //===================================================================== + ImageReader::~ImageReader() + { + // std::cout << "#### ImageReader::~ImageReader()"<::iterator i; + for (i=mReader.begin(); i!=mReader.end(); i++) + { + // std::cout << "#### ImageReader::UnRegister(" + // << (*i)->GetName()<<")"<Delete(); + mUnreadableImage = 0; + } + } + //===================================================================== + + //===================================================================== + void ImageReader::Register(SpecificImageReader* r) + { + // std::cout << "#### ImageReader::Register("<GetName()<<")"<PushBackExtensions(mKnownExtensions); + } + //===================================================================== + + //===================================================================== + // Returns true iff the file is readable + bool ImageReader::CanRead( const std::string& filename, + const std::string& exclude ) + { + // std::cout << "## ImageReader::CanRead("<::iterator i; + for (i=mReader.begin(); i!=mReader.end(); i++) + { + if ((*i)->GetName()==exclude) continue; + ok = (*i)->CanRead(filename); + if (ok) + { + mLastFilename = filename; + mLastReader = *i; + break; + } + } + return ok; + } + //===================================================================== + + //===================================================================== + // Reads the file (CanRead must be called before : no test here) + vtkImageData* ImageReader::ReadImage( const std::string& filename, + const std::string& exclude ) + { + // std::cout << "## ImageReader::Read("<ShallowCopy(mUnreadableImage); + return im; + } + } + vtkImageData* i = mLastReader->ReadImage(mLastFilename); + // std::cout << "i="<ShallowCopy(mUnreadableImage); + } + // std::cout << "i="<& attr) + { + // std::cout << "ImageReader::ReadDicomInfo '"<ReadAttributes(mLastFilename,attr); + } + //===================================================================== + + + +} // namespace creaImageIO diff --git a/src2/creaImageIOImageReader.h b/src2/creaImageIOImageReader.h new file mode 100644 index 0000000..e36f226 --- /dev/null +++ b/src2/creaImageIOImageReader.h @@ -0,0 +1,70 @@ +#ifndef __creaImageIOImageReader_h_INCLUDED__ +#define __creaImageIOImageReader_h_INCLUDED__ + + +#include + +#include +#include +#include +#include + +namespace creaImageIO +{ + + + //===================================================================== + /// Image reader of a specific image format + class SpecificImageReader; + //===================================================================== + + //===================================================================== + /// Generic image reader which stores a vector of SpecificImageReader + class ImageReader + { + public: + ImageReader(); + ~ImageReader(); + + /// Returns the known extensions + const std::vector& GetKnownExtensions() + { return mKnownExtensions; } + /// Returns true iff the file is readable + bool CanRead( const std::string& filename, + const std::string& exclude = ""); + /// Reads and returns the image data. + /// Returns an "Unreadable image" picture if fails + vtkImageData* ReadImage( const std::string& filename, + const std::string& exclude = ""); + + /// Reads the attributes of the image. + /// Requested attributes names are provided as keys + /// in a string to string map + /// On return, the values of the map are the values + /// of the attributes (empty string if not available). + void ReadAttributes(const std::string& filename, + std::map& attr); + + protected: + + void Register(SpecificImageReader*); + + std::vector mReader; + std::vector mKnownExtensions; + vtkImageData* mUnreadableImage; + + std::string mLastFilename; + SpecificImageReader* mLastReader; + + private: + + }; // class ImageReader + //===================================================================== + + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOImageReader_h_INCLUDED__ diff --git a/src2/creaImageIOIndexedHeap.h b/src2/creaImageIOIndexedHeap.h new file mode 100644 index 0000000..138fc56 --- /dev/null +++ b/src2/creaImageIOIndexedHeap.h @@ -0,0 +1,139 @@ +/* + +*/ +/*! \file + \brief Indexed priority queues handled by binary trees. +*/ +#ifndef __creaImageIOIndexedHeap_h_INCLUDED__ +#define __creaImageIOIndexedHeap_h_INCLUDED__ + +#include + +namespace creaImageIO +{ + + + + template */, + class Indexer/*=Index */> + class IndexedHeap ; + template < class T, + class C, + class I> + std::ostream& operator << (std::ostream&, const IndexedHeap& ); + + + //template , class Index=IndexIndex > class SlicedIndexedHeap; + + + //======================================================================== + /// \brief Indexed priority queues handled by binary trees. + /// + /// Heap Allows : + /// - log(n) insertion + /// - constant time acces to the first element + /// - log(n) removal of the first element + /// - log(n) priority change of a random element + /// Indexation Allows : + /// - constant time access to a random element (for priority change) + /// + /// The Indexer is an unary_function whose operator()(T& t) + /// returns a reference on an integer which + /// is maintained by the IndexedHeap in order to provide at any time + /// the position of the object t in the Heap + /// (hence allowing constant time random access to an object). + template */, + class Indexer /*=Index*/> + class IndexedHeap + { + // friend class SlicedIndexedHeap; + public : + + //====================================================================== + /// Constructor + IndexedHeap () {} + /// Constructor + IndexedHeap ( const Comparator& comp, const Indexer& ind ) ; + /// Destructor + ~IndexedHeap() { } + /// Sets the comparator + void set( const Comparator& comp ); + /// Sets the Index + void set( const Indexer& ind ); + //====================================================================== + + //====================================================================== + /// inserts an element in the Heap and returns its position + int insert(T); + /// return a reference on the first element of the Heap + T& top(); + /// return a constant reference on the first element of the Heap + const T& top() const; + /// removes and returns the first element of the Heap + T remove_top(); + /// removes and returns the nth element + T remove(int n); + /// returns the size of the Heap + inline int size() const {return m_p.size(); } + /// empties the Heap + void clear(); + //====================================================================== + + //====================================================================== + /// returns a constant on the stack of elements + const std::vector & stack() const {return m_p;} + /// returns a reference to the ith element of the stack + T& operator [] (int i) { return m_p[i];} + /// returns a constant reference to the ith element of the stack + const T& operator [] (int i) const { return m_p[i];} + /// returns the index (position) of t + inline int index(T& t) { return (*m_i)(t); } + //====================================================================== + + //====================================================================== + /// returns the position of the father of i + inline int father( int i ) const; + /// returns the position of the right son of i + inline int rightson( int i ) const; + /// returns the position of the leftson of i + inline int leftson( int i ) const; + //====================================================================== + /// swaps ith and jth elements + inline void swap(int i, int j); + /// remonte un element dans le tas tant qu'il n'est pas a sa place. + /// renvoie la position finale + inline int upsort(int); + /// descend un element dans le tas tant qu'il n'est pas a sa place. + /// renvoie la position finale + inline int downsort(int); + //====================================================================== + + protected : + /// binary tree handled by a vector + std::vector m_p; + /// comparator pointer + const Comparator* m_c; + /// Index pointer + const Indexer* m_i; + }; + //======================================================================== + // EO class IndexedHeap + //======================================================================== + + +#include "creaImageIOIndexedHeap.txx" + + +}; +//=========================================================================== +// EO namespace creaImageIO +//=========================================================================== + + + +//=========================================================================== +// EOF +//=========================================================================== +#endif diff --git a/src2/creaImageIOMultiThreadImageReader.cpp b/src2/creaImageIOMultiThreadImageReader.cpp new file mode 100644 index 0000000..fd76051 --- /dev/null +++ b/src2/creaImageIOMultiThreadImageReader.cpp @@ -0,0 +1,590 @@ +#include +#include +#include + +namespace creaImageIO +{ + + //===================================================================== + void MultiThreadImageReaderUser::MultiThreadImageReaderSendEvent + ( const std::string& filename, + EventType type, + vtkImageData* image) + { + wxMutexLocker lock(mMultiThreadImageReaderUserMutex); + this->OnMultiThreadImageReaderEvent(filename,type,image); + } + //===================================================================== + + //===================================================================== + class ThreadedImageReader: public wxThread + { + public: + ThreadedImageReader(MultiThreadImageReader* tir) : + mMultiThreadImageReader(tir) + {} + + void* Entry(); + void OnExit(); + + vtkImageData* Read(const std::string& filename); + + + private: + ImageReader mReader; + MultiThreadImageReader* mMultiThreadImageReader; + }; + //===================================================================== + + + //===================================================================== + MultiThreadImageReader::MultiThreadImageReader(int number_of_threads) + : //mDoNotSignal(false), + mReader(0), + mTotalMem(0), + mTotalMemMax(10000) + { + // std::cout << "#### MultiThreadImageReader::MultiThreadImageReader(" + // << " #threads= " << number_of_threads <<" )"<Create(); + if ( (*i)->Run() != wxTHREAD_NO_ERROR ) + { + std::cout << "ERROR starting a thread"<< std::endl; + return false; + } + else + { + // std::cout << " ===> Thread "<<(*i)->GetCurrentId() + // <<" successfully created"<< std::endl; + + } + } + wxMutexLocker locker(GetMultiThreadImageReaderUserMutex()); + // std::cout << "EO Start : #Threads running = " + // << mNumberOfThreadedReadersRunning<Delete(); + } + mThreadedImageReaderList.clear(); + // Wait a little to be sure that all threads have stopped + // A better way to do this ? + // wxMilliSleep(1000); + // New method : the threads generate a stop event when they have finished + // We wait until all threads have stopped +// std::cout << "Waiting for stop signals..."<first; + } + mImages.clear(); + } + //===================================================================== + + //===================================================================== + MultiThreadImageReader::~MultiThreadImageReader() + { + // std::cout << "#### MultiThreadImageReader::~MultiThreadImageReader()" + // <UnloadIndex()<0) return; + int old_prio = p->GetPriority(); + if (priority > old_prio) + { + p->SetPriority(priority); + mUnloadQueue.downsort(p->UnloadIndex()); + } + else if ( old_prio > priority ) + { + p->SetPriority(priority); + mUnloadQueue.upsort(p->UnloadIndex()); + } + } + //===================================================================== + + //===================================================================== + void MultiThreadImageReader::Request( MultiThreadImageReaderUser* user, + const std::string& filename, + int priority ) + { + wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex); + + if (mThreadedImageReaderList.size()==0) + { + // no detached reader : use self reader + ImageToLoad itl(user,filename); + ImageMapType::iterator i = mImages.find(&itl); + if (i!=mImages.end()) + { + ImageToLoadPtr pitl = const_cast(i->first); + // Already inserted + if (pitl->GetImage() != 0) + { + // Already read + pitl->SetUser(user); + UpdateUnloadPriority(pitl,priority); + SignalImageRead(pitl,false); + return; // pitl->GetImage(); + } + } + ImageToLoadPtr pitl = new ImageToLoad(user,filename,0); + mImages[pitl] = 0; + pitl->SetImage(mReader->ReadImage(filename)); + UpdateUnloadPriority(pitl,priority); + SignalImageRead(pitl,true); + // return pitl->GetImage(); + return; + } + + + ImageToLoad itl(user,filename); + ImageMapType::iterator i = mImages.find(&itl); + if (i!=mImages.end()) + { + // Already inserted + if (i->first->GetImage() != 0) + { + // Already read : ok :signal the user + UpdateUnloadPriority(i->first,priority); + SignalImageRead(i->first,false); + return; + } + /// Already requested : change the priority + ImageToLoadPtr pitl = const_cast(i->first); + pitl->SetPriority(priority); + // Already in queue + if (pitl->Index()>=0) + { + // Re-sort the queue + mQueue.upsort(pitl->Index()); + } + // Not read but not in queue = being read = ok + else + { + + } + } + else + { + // Never requested before or unloaded + ImageToLoadPtr pitl = new ImageToLoad(user,filename,priority); + mImages[pitl] = 0; + mQueue.insert(pitl); + } + } + //===================================================================== + + //===================================================================== + void MultiThreadImageReader::OnMultiThreadImageReaderEvent + (const std::string& filename, + MultiThreadImageReaderUser::EventType e, + vtkImageData* image) + { + if ((e==MultiThreadImageReaderUser::ImageLoaded) && + (filename == mRequestedFilename)) + { + mRequestedImage = image; + } + else if (e==MultiThreadImageReaderUser::ThreadedReaderStarted) + { + mNumberOfThreadedReadersRunning++; + // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl; + } + else if (e==MultiThreadImageReaderUser::ThreadedReaderStopped) + { + mNumberOfThreadedReadersRunning--; + // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl; + } + } + //===================================================================== + + //===================================================================== + vtkImageData* MultiThreadImageReader::GetImage(const std::string& filename) + { + + // std::cout << "** MultiThreadImageReader::GetImage('"<(i->first); + // Already inserted + if (pitl->GetImage() != 0) + { + // Already read + UpdateUnloadPriority(pitl, + GetMaximalPriorityWithoutLocking()+1); + return pitl->GetImage(); + } + } + ImageToLoadPtr pitl = new ImageToLoad(this,filename,0); + mImages[pitl] = 0; + pitl->SetImage(mReader->ReadImage(filename)); + UpdateUnloadPriority(pitl, + GetMaximalPriorityWithoutLocking()+1); + return pitl->GetImage(); + } + + mRequestedFilename = filename; + mRequestedImage = 0; + ImageToLoad itl(this,filename); + ImageMapType::iterator i = mImages.find(&itl); + if (i!=mImages.end()) + { + // Already inserted in queue + if (i->first->GetImage() != 0) + { + // Already read : ok : return it + return i->first->GetImage(); + } + /// Already requested : change the priority + ImageToLoadPtr pitl = const_cast(i->first); + pitl->SetPriority( GetMaximalPriorityWithoutLocking() + 1 ); + pitl->SetUser( this ); + // Already in queue + if (pitl->Index()>=0) + { + // Re-sort the queue + mQueue.upsort(pitl->Index()); + } + // Not read but not in queue = being read = ok + else + { + pitl->SetUser( this ); + } + } + else + { + + // Never requested before or unloaded + ImageToLoadPtr pitl = + new ImageToLoad(this,filename, + GetMaximalPriorityWithoutLocking() + 1); + mImages[pitl] = 0; + mQueue.insert(pitl); + } + } + while (0); + + // std::cout << "Waiting..."< Total mem = "< mTotalMemMax) + { + // std::cout + // <<" ! Exceeded max of " + // << mTotalMemMax << " : unloading oldest image ... " + // << std::endl; + if ( mUnloadQueue.size() <= 1 ) + { + // std::cout << "Only one image : cannot load AND unload it !!" + // <GetUser(); + + if ((user!=0)&&(user!=this)) + { + user->GetMultiThreadImageReaderUserMutex().Lock(); + } + + + // std::cout << "'" << unload->GetFilename() << "'" << std::endl; + mTotalMem -= unload->GetImage()->GetEstimatedMemorySize(); + // std::cout << " ==> Total mem = "<GetFilename(); + if (unload->Index()>=0) + { + // std::cout << "still in queue"<Index() = -1; + + + ImageMapType::iterator it = mImages.find(unload); + if (it!=mImages.end()) + { + mImages.erase(it); + } + // std::cout << "delete..."<GetMultiThreadImageReaderUserMutex().Unlock(); + // std::cout << "event"<MultiThreadImageReaderSendEvent + (filename, + MultiThreadImageReaderUser::ImageUnloaded, + 0); + // std::cout << "event ok"<0) + { + max = mQueue.top()->GetPriority(); + } + if (mUnloadQueue.size()>0) + { + int max2 = mUnloadQueue.top()->GetPriority(); + if (max2>max) max=max2; + } + return max; + } + //===================================================================== + + + //===================================================================== + //===================================================================== + //===================================================================== + //===================================================================== + + //===================================================================== + void* ThreadedImageReader::Entry() + { + // std::cout << "### Thread "<MultiThreadImageReaderSendEvent + ("", + MultiThreadImageReaderUser::ThreadedReaderStarted, + 0); + + // While was not deleted + while (!TestDestroy()) + { + // std::cout << "### Thread "<MultiThreadImageReaderEventLock(); + //mMutex.Lock(); + // If image in queue + if (mMultiThreadImageReader->mQueue.size()>0) + { + MultiThreadImageReader::ImageToLoadPtr i = + mMultiThreadImageReader->mQueue.remove_top(); + + mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); + //mMutex.Unlock(); + + + // std::cout << "### Thread "<GetFilename() << "'" << std::endl; + + // Do the job + vtkImageData* im = Read(i->GetFilename()); + + // Store it in the map + mMultiThreadImageReader->MultiThreadImageReaderEventLock(); + //mMutex.Lock(); + MultiThreadImageReader::ImageToLoad itl(0,i->GetFilename()); + MultiThreadImageReader::ImageMapType::iterator it = + mMultiThreadImageReader->mImages.find(&itl); + MultiThreadImageReader::ImageToLoadPtr + pitl = const_cast + (it->first); + pitl->SetImage(im); + mMultiThreadImageReader->SignalImageRead(pitl,true);//i->GetFilename()); + mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); //mMutex.Unlock(); + + // std::cout << "### Thread "<GetFilename() << "' : DONE" << std::endl; + + } + else + { + mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); + //mMutex.Unlock(); + // Wait a little to avoid blocking + Sleep(10); + } + }; + // std::cout << "### Thread "<MultiThreadImageReaderSendEvent + ("", + MultiThreadImageReaderUser::ThreadedReaderStopped, + 0); + } + //===================================================================== + + //===================================================================== + vtkImageData* ThreadedImageReader::Read(const std::string& filename) + { + return mReader.ReadImage(filename); + } + //===================================================================== + +} // namespace creaImageIO diff --git a/src2/creaImageIOMultiThreadImageReader.h b/src2/creaImageIOMultiThreadImageReader.h new file mode 100644 index 0000000..d72981d --- /dev/null +++ b/src2/creaImageIOMultiThreadImageReader.h @@ -0,0 +1,246 @@ +#ifndef __creaImageIOThreadedImageReader_h_INCLUDED__ +#define __creaImageIOThreadedImageReader_h_INCLUDED__ + +#include +#include +#include +#include +#include +#include +#include + +namespace creaImageIO +{ + + //===================================================================== + class ThreadedImageReader; + class MultiThreadImageReader; + //===================================================================== + + //===================================================================== + class CREAIMAGEIO_EXPORT MultiThreadImageReaderUser + { + public: + friend class ThreadedImageReader; + friend class MultiThreadImageReader; + + MultiThreadImageReaderUser() {} + virtual ~MultiThreadImageReaderUser() {} + + typedef enum + { + ThreadedReaderStarted, + ThreadedReaderStopped, + ImageLoaded, + ImageUnloaded, + Error + } + EventType; + /// The virtual method to overload by MultiThreadImageReader users + /// It is called when an image has been loaded or unloaded + /// Provides : + /// * The image file name which was requested + /// * The type of event + /// * If type==ImageLoaded the image pointer, else NULL pointer + virtual void OnMultiThreadImageReaderEvent( const std::string& filename, + EventType type, + vtkImageData* image) + {} + inline void MultiThreadImageReaderEventLock() + { mMultiThreadImageReaderUserMutex.Lock(); } + inline void MultiThreadImageReaderEventUnlock() + { mMultiThreadImageReaderUserMutex.Unlock(); } + inline wxMutex& GetMultiThreadImageReaderUserMutex() + { return mMultiThreadImageReaderUserMutex; } + private: + /// + void MultiThreadImageReaderSendEvent( const std::string& filename, + EventType type, + vtkImageData* image); + wxMutex mMultiThreadImageReaderUserMutex; + }; + //===================================================================== + + //===================================================================== + /// + /// TAKE CARE : For the moment it only supports a **SINGLE USER** + class MultiThreadImageReader : public MultiThreadImageReaderUser + { + public: + friend class ThreadedImageReader; + + /// Ctor with the number of threads to use + MultiThreadImageReader(int number_of_threads = 1); + /// Dtor + ~MultiThreadImageReader(); + + /// Starts the reader = create the threads which start to check + /// periodically the queue of requested images to read + bool Start(); + /// Stops the reader = stops the threads and delete the images loaded + void Stop(); + + /// Request the image "filename" with a given priority + /// When the image is ready (or an error occurred) + /// The observer's callback is invoked + void Request( MultiThreadImageReaderUser* user, + const std::string& filename, + int priority ); + + /// Request the image "filename" immediately + /// Blocks until image loaded + /// (no user callback but image returned) + vtkImageData* GetImage(const std::string& filename); + + /// + int GetMaximalPriority(); + + /// + void OnMultiThreadImageReaderEvent( const std::string& filename, + EventType type, + vtkImageData* image); + + protected: + int GetMaximalPriorityWithoutLocking(); + // + class ImageToLoad + { + public: + ImageToLoad( MultiThreadImageReaderUser* user, + const std::string& filename, + int prio=0) + : mUser(user), + mFilename(filename), + mPriority(prio), + mIndex(-1), + mUnloadIndex(-1), + mImage(0) + {} + ~ImageToLoad() + { + if (mImage>0) + { + // std::cout << "Refs = "<GetReferenceCount()<Delete(); + } + } + MultiThreadImageReaderUser* GetUser() const { return mUser; } + void SetUser( MultiThreadImageReaderUser* u ) { mUser = u; } + const std::string& GetFilename() const { return mFilename; } + int GetPriority() const { return mPriority; } + void SetPriority(int p) { mPriority=p; } + int& Index() { return mIndex; } + int& UnloadIndex() { return mUnloadIndex; } + vtkImageData* GetImage() const { return mImage; } + void SetImage( vtkImageData* i ) { mImage=i; } + private: + MultiThreadImageReaderUser* mUser; + std::string mFilename; + int mPriority; + int mIndex; + int mUnloadIndex; + vtkImageData* mImage; + }; + // + + /// Type of pointer on an ImageToLoad struct + typedef ImageToLoad* ImageToLoadPtr; + + /// ImageToLoadPtr comparator on priority (for image queue) + struct ImageToLoadPtrPriorityComparator + { + bool operator() (ImageToLoadPtr const & a, ImageToLoadPtr const & b) + const + { + return ( a->GetPriority() > b->GetPriority() ); + } + }; + /// ImageToLoadPtr comparator on inverse priority (for image to unload queue) + struct ImageToLoadPtrInversePriorityComparator + { + bool operator() (ImageToLoadPtr const & a, ImageToLoadPtr const & b) + const + { + return ( a->GetPriority() < b->GetPriority() ); + } + }; + + + /// ImageToLoadPtr comparator on filename (for image map) + struct ImageToLoadPtrFilenameComparator + { + bool operator() (ImageToLoadPtr const & a, ImageToLoadPtr const & b) + const + { + return ( a->GetFilename() < b->GetFilename() ); + } + }; + + /// ImageToLoadPtr indexer for image queue + struct ImageToLoadPtrIndexer + { + int& operator()(ImageToLoadPtr & t) const { return t->Index(); } + }; + /// ImageToLoadPtr indexer for to unload image queue + struct ImageToUnloadPtrIndexer + { + int& operator()(ImageToLoadPtr & t) const { return t->UnloadIndex(); } + }; + + /// The callback from threaded readers when an image is read + void SignalImageRead(ImageToLoadPtr p, bool purge); + + /// The type of map of images + typedef std::map ImageMapType; + /// The map of images + ImageMapType mImages; + /// Comparator for the image to load queue + ImageToLoadPtrPriorityComparator mComparator; + /// Indexer for the image to load queue + ImageToLoadPtrIndexer mIndexer; + /// The image to load priority queue + IndexedHeap mQueue; + + /// The type of list of threaded readers + typedef std::vector ThreadedImageReaderListType; + ThreadedImageReaderListType mThreadedImageReaderList; + /// The number of currently running threaded readers + int mNumberOfThreadedReadersRunning; + /// The mutex used to access safely internal data from any thread + /// LG : Removed ! We now use the embedded mutex in User from which + /// we inherit... + // wxMutex mMutex; + + /// For GetImage : the filename requested + std::string mRequestedFilename; + /// For GetImage : the image requested + vtkImageData* mRequestedImage; + + /// If number of threads == 0 then uses an internal non-threaded reader + ImageReader* mReader; + + /// The type of list of images loaded + /// used to unload oldest image when memory limit exceeded + /// The image to unload priority queue + IndexedHeap mUnloadQueue; + + void UpdateUnloadPriority(ImageToLoadPtr p, int priority); + long mTotalMem; + long mTotalMemMax; + + + }; // class MultiThreadImageReader + //===================================================================== + + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOThreadedImageReader_h_INCLUDED__ diff --git a/src2/creaImageIOSQLiteTreeHandler.cpp b/src2/creaImageIOSQLiteTreeHandler.cpp new file mode 100644 index 0000000..509aa90 --- /dev/null +++ b/src2/creaImageIOSQLiteTreeHandler.cpp @@ -0,0 +1,979 @@ +#include + +#include "CppSQLite3.h" + +#include + +//#include + +//#include + +//#include + +#include + +#include "wx/wx.h" +#include +#include + + +//#include + +#include +#include +using namespace crea; + +#include +#include + +namespace creaImageIO +{ + using namespace tree; + + + //============================================================= + SQLiteTreeHandler::SQLiteTreeHandler(const std::string& filename) + : mFileName(filename) + { + /* + mSQLiteTreeHandler = this; + NodeTypeDescription::FieldDescriptionMapType& M = + mNodeTypeDescription[Node::Database].GetFieldDescriptionMap(); + + boost::filesystem::path full_path(location); + mName = full_path.leaf(); + Field::Description fname("Name",0,0,"Name",0); + M[fname.key] = fname; + UnsafeSetFieldValue(fname.key,mName); + Field::Description flocation("File name",0,0,"File name",0); + M[flocation.key] = flocation; + UnsafeSetFieldValue(flocation.key,location); + */ + // Field::Description ftype("Type",0,0,"Type",0); + // M[ftype.key] = ftype; + // UnsafeSetFieldValue(ftype.key,"Invalid location"); + + mDB = new CppSQLite3DB; + // std::cout << "** SQLite Version: " << mDB->SQLiteVersion() << std::endl; + + + } + //============================================================= + + //============================================================= + SQLiteTreeHandler::~SQLiteTreeHandler() + { + delete mDB; + /* + Already done in Node now that SQLiteTreeHandler inherits from it + ChildrenListType::iterator i; + for (i=GetChildrenList().begin(); i!=GetChildrenList().end(); i++) + { + delete *i; + } + */ + } + //============================================================= + + + //============================================================= + void SQLiteTreeHandler::BuildDefaultTreeDescription() + { + /* + for (int i = Node::Patient; + i <= Node::Image; + ++i) + { + mNodeTypeDescription[i].BuildDefault(i); + } + */ + + // TODO : GetTree().GetDescription().LoadXML(FILE); + } + //============================================================= + + //============================================================= + // void SQLiteTreeHandler::Print() const + // { + /* + std::cout << "-> '"<Print(); + } + */ + // } + //============================================================= + + //===================================================================== + /* + bool SQLiteTreeHandler::LocationIsValid() + { + // TO DO + return true; + } + */ + //===================================================================== + + //===================================================================== + char* format_sql(const std::string& s) + { + return sqlite3_mprintf("%q",s.c_str()); + } + //===================================================================== + + // sqlite3_exec(db, zSQL, 0, 0, 0); + // sqlite3_free(zSQL); + // char* CHAIN = format_sql(QUER); \ +// sqlite3_free(CHAIN); \ + + //===================================================================== +#define QUERYDB(QUER,RES) \ + try \ + { \ + RES = mDB->execQuery(QUER.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + std::cout << "SQLite query '"<execDML(UP.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + std::cout << "SQLite update '"< SQLiteTreeHandler::Open('"< SQLiteTreeHandler::New('"<& attr ) + { + return -1; + } + //===================================================================== + + + //===================================================================== + bool SQLiteTreeHandler::Remove(tree::Node*) + { + return false; + } + //===================================================================== + + + + + + + + //===================================================================== + bool SQLiteTreeHandler::DBOpen() + { + // std::cout << "### Opening SQLite database '"<open(GetFileName().c_str()); + } + catch (CppSQLite3Exception& e) + { + std::cerr << "Opening '"<open(GetFileName().c_str()); + } + catch (CppSQLite3Exception& e) + { + creaMessage("Gimmick!",1, + "[Gimmick!] !! ERROR '" + << e.errorCode() << ":" + << e.errorMessage() < keys; + NodeTypeDescription::FieldDescriptionMapType::iterator i; + for (i = GetNodeTypeDescription(c).GetFieldDescriptionMap().begin(); + i != GetNodeTypeDescription(c).GetFieldDescriptionMap().end(); + ++i) + { + if (i->second.flags==1) continue; + keys.push_back(&(i->second.key)); + } + std::vector::iterator j; + for (j=keys.begin();j!=keys.end();) + { + s += **j + " text"; + ++j; + if (j!=keys.end()) + s += ",\n"; + } + */ + } + //===================================================================== + + //===================================================================== + void SQLiteTreeHandler::DBExportTreeDescription() + { + /* + // std::cout<<"ExportNodeTypeDescriptionsToDB()"<tableExists(SQLiteTreeHandlerStructure::Table(i)); + if (ok) + { + // std::cout << "** Table "<ChildrenLoaded() ) + { + // std::cout << "--> Children already loaded"<GetType() == Node::Image ) + { + return nbloaded; + } + if ( xparent->GetType() >= maxlevel ) + { + return nbloaded; + } + + // msw[2].Pause(); + // msw[2].Resume(); + + Node::Type type = xparent->GetType()+1; + + // Query DB + + std::string query = "SELECT * FROM "; + query += GetTree().GetDescriptor().GetLevelDescriptor(level).GetName(); + //SQLiteTreeHandlerStructure::Table(type); + if (parent!=0) + { + query += " WHERE PARENT_ID='" + parent->GetFieldValue("ID") + "'"; + } + + // std::cout << "** SQL = '"<SetFieldValue(q.fieldName(fld),q.getStringField(fld)); + } + // Index + TypeId ti; + ti.type = type; + ti.id = n->GetFieldValue("ID"); + mTypeIdToNodeMap[ti] = n; + // recurse + if ( type < maxlevel ) + { + msw[2].Pause(); + nbloaded += DBLoadChildren(n,maxlevel); + msw[2].Resume(); + } + // next entry in db + q.nextRow(); + } + + xparent->SetChildrenLoaded(true); + + // msw[2].Pause(); + return nbloaded; + } + //===================================================================== + + + + + + + //===================================================================== + bool SQLiteTreeHandler::DBInsert(Node* alien_node, + UpdateSummary& summary) + { + // std::cout << "SQLiteTreeHandler::Insert('"<GetLabel() + // <<"')"<GetLabel()<<"'" + // << std::endl; + // Load the patients if not already done + DBLoadChildren(this,Node::Patient); + + parent_id = ""; + Node* parent = this; + + // The chain of ancestors + std::deque chain; + Node* cur = alien_node->GetParent(); + for (int type=Node::Patient; + typeGetType();++type) + { + chain.push_front(cur); + cur = cur->GetParent(); + } + + // create the nodes if do not exist + std::deque::iterator i; + for (i=chain.begin();i!=chain.end();++i) + { + // std::cout << " cur = '"<<(*i)->GetLabel()<<"'"<GetType()+1); + + parent_id = cur_id; + } + return parent; + } + //===================================================================== + + + + //===================================================================== + void SQLiteTreeHandler::DBRecursiveGetOrCreateNode(Node* alien_node, + Node* parent, + const std::string& parent_id, + UpdateSummary& summary) + { + // std::cout << "SQLiteTreeHandler::RecursiveGetOrCreateNode('" + // <GetLabel() + // <<"','"<GetChildrenList().begin(); + i != alien_node->GetChildrenList().end(); + i++) + { + DBRecursiveGetOrCreateNode((*i),new_node,new_id,summary); + } + } + //===================================================================== + + + //===================================================================== + Node* SQLiteTreeHandler::DBGetOrCreateNode(Node* alien_node, + Node* internal_parent, + std::string parent_id, + std::string& node_id, + UpdateSummary& summary) + { + // std::cout << "DBGetOrCreateNode('"<GetLabel()<<"','" + // << internal_parent << "','"<< parent_id<<"')"<0) + { + node_id = node->UnsafeGetFieldValue("ID"); + return node; + } + // Second : try in DB + + // Does not exist : Create new one + node = new Node(alien_node->GetType(),this,internal_parent); + node->SetChildrenLoaded(true); + // Copy fields values from alien + Node::FieldValueMapType::iterator i,j; + for (i = node->GetFieldValueMap().begin(); + i != node->GetFieldValueMap().end(); + i++) + { + j = alien_node->GetFieldValueMap().find(i->first); + if (j != alien_node->GetFieldValueMap().end() ) + { + i->second = j->second; + } + } + + msw[2].Resume(); + if (node->GetType()!=Node::Patient) + node->SetFieldValue("PARENT_ID",parent_id); + + // Insert in DB + std::string val; + BuildSQLFieldsValues(node,val); + std::string insert("INSERT INTO "); + insert += std::string(SQLiteTreeHandlerStructure::Table(node->GetType())) + + " " + val + ";"; + // std::cout << "** SQL = '"<lastRowId(); + std::stringstream ri; + ri << mDB->lastRowId(); + node_id = ri.str(); + // std::cout << "LastRowId='"<lastRowId()<<"' vs '"<SetFieldValue("ID",node_id); + // Insert in TypeId map + TypeId ti; + ti.type = node->GetType(); + ti.id = node_id; + mTypeIdToNodeMap[ti] = node; + // std::cout << "== Insert TypeId ("<GetType()==Node::Patient) summary.added_patients++; + if (node->GetType()==Node::Study) summary.added_studies++; + if (node->GetType()==Node::Series) summary.added_series++; + if (node->GetType()==Node::Image) summary.added_images++; + + return node; + } + //===================================================================== + + //===================================================================== + Node* SQLiteTreeHandler::GetChildrenLike(Node* parent, + Node* alien_node) + { + Node::ChildrenListType::iterator i; + for (i = parent->GetChildrenList().begin(); + i != parent->GetChildrenList().end(); + i++) + { + Node::Type type = alien_node->GetType(); + bool ok = true; + for (int j=0; + jGetFieldValue(SQLiteTreeHandlerStructure:: + QueryField(type,j).key ) != + (*i)->GetFieldValue(SQLiteTreeHandlerStructure:: + QueryField(type,j).key ) ) + { + ok = false; + break; + } + } + if (ok) + { + return (*i); + } + } + return 0; + } + //===================================================================== + + //===================================================================== + std::string SQLiteTreeHandler::DBGetNodeId(Node* node, + const std::string& parent_id) + { + // std::cout << "SQLiteTreeHandler::DBGetNodeId('"<GetLabel() + // <<"','"<GetType(); + + std::string table = SQLiteTreeHandlerStructure::Table(type); + std::string where = "WHERE "; + + if (type!=Node::Patient) + { + where += "PARENT_ID='" + parent_id + //node->GetFieldValue("PARENT_ID") + + "' AND "; + } + + for (int i=0;iGetParent()) + { + node->GetParent()->RemoveChildrenFromList(node); + } + delete node; + // std::cout << "DELETE OK"<GetLabel()<<"')"<GetType()); + query += " WHERE ID='"+ node->GetFieldValue("ID") + "';"; + + UPDATEDB(query); + + Node::ChildrenListType::iterator i; + for (i = node->GetChildrenList().begin(); + i != node->GetChildrenList().end(); + i++) + { + DBRecursiveRemoveNode((*i)); + } + } + //===================================================================== + + //===================================================================== + int SQLiteTreeHandler::DBQueryNumberOfChildren(Node* node) + { + std::string query = "SELECT COUNT (ID) FROM "; + query += SQLiteTreeHandlerStructure::Table(node->GetType()+1); + if (node->GetType() != Node::Database) + { + query += " WHERE PARENT_ID='"+ node->GetFieldValue("ID")+"'"; + } + query += ";"; + + // std::cout << "**SQL = "<< query << std::endl; + + CppSQLite3Query q; + QUERYDB(query,q); + + // std::cout << "**RES = "<< q.getIntField(0) <GetLabel()<<"')"<GetFieldValueMap().begin(); + i != n->GetFieldValueMap().end(); + i++) + { + if (i->first=="ID") + { + continue; + } + // std::cout << "("<first<<","<second<<")"<first + "'"; + values += "'" + format_sql2(i->second) + "'"; + atts += ","; + values += ","; + } + atts[atts.size()-1]=' '; + values[values.size()-1]=' '; + + str = "("+atts+") VALUES ("+values+")"; + + } + //===================================================================== +*/ + + + +} // namespace creaImageIO diff --git a/src2/creaImageIOSQLiteTreeHandler.h b/src2/creaImageIOSQLiteTreeHandler.h new file mode 100644 index 0000000..516910e --- /dev/null +++ b/src2/creaImageIOSQLiteTreeHandler.h @@ -0,0 +1,190 @@ +#ifndef __creaImageIOSQLiteTreeHandler_h_INCLUDED__ +#define __creaImageIOSQLiteTreeHandler_h_INCLUDED__ + +#include + +class CppSQLite3DB; + +namespace creaImageIO +{ + + + //======================================================================= + /// Concrete TreeHandler which manages a tree stored in a sqlite database + class SQLiteTreeHandler : virtual public TreeHandler + { + public: + ///==================================================================== + /// Ctor with database file name + SQLiteTreeHandler(const std::string& filename); + /// Dtor + virtual ~SQLiteTreeHandler(); + ///==================================================================== + + ///==================================================================== + /// Returns the sqlite db file name + const std::string& GetFileName() const { return mFileName; } + ///==================================================================== + + ///==================================================================== + /// QUERY METHODS + /// Is the 'source' readable ? + virtual bool IsReadable() { return true; } + /// Is the 'source' writable ? + virtual bool IsWritable() { return true; } + ///==================================================================== + + + ///==================================================================== + /// INITIALIZATION / FINALIZATION + ///==================================================================== + + ///==================================================================== + /// Opens an existing 'source' + /// Default mode is read only + /// If IsWritable and writable==true then opens in read/write mode + virtual bool Open(bool writable = false); + /// Closes the 'source' + virtual bool Close(); + /// Creates a new 'source' + /// Default mode is read only + /// If IsWritable and writable==true then opens in read/write mode + virtual bool Create(bool writable = false); + /// Destroys the 'source' + virtual bool Destroy(); + ///==================================================================== + + + ///==================================================================== + // READ METHODS + ///==================================================================== + + + ///==================================================================== + /// Returns the number of children of the Node *WITHOUT LOADING THEM* + /// REM : The Tree itself is a Node and asking for its number of + /// children returns the number of children of level 1. + virtual unsigned int GetNumberOfChildren(tree::Node* n); + ///==================================================================== + + ///==================================================================== + /// Recursively loads the children of node 'parent' until maxlevel + /// is reached. + /// If parent == NULL or parent == tree then starts with the 'children' of + /// the tree itself. + /// Returns the total number of children loaded. + virtual int LoadChildren(tree::Node* parent, int maxlevel); + ///==================================================================== + + ///==================================================================== + /// Unloads the Node and its descendants + /// WITHOUT altering the source, e.g. the database + virtual void UnLoad(tree::Node* n); + ///==================================================================== + + + ///==================================================================== + /// WRITE METHODS : WORK ONLY IN WRITE MODE + ///==================================================================== + /// Adds a branch in the tree with the attributes provided + /// returns the Level in the tree where the branch was connected + /// (-1 for error, 0 for top level, etc. ) + /// Of course the branch is loaded on exit + virtual int AddBranch + ( const std::map& attr ); + /// Removes the node and its descendants + virtual bool Remove(tree::Node*); + ///==================================================================== + + + + protected: + // Open + bool DBOpen(); + void DBImportTreeDescription(); + + // Creation + /// Creates a new database on disk and the tables + bool DBCreate(); + /// Creates the default tree description for a new database + void BuildDefaultTreeDescription(); + /// Appends to string s the SQL command to create the attributes of a given level + void AppendAttributesSQLDefinition(int level, std::string& s); + void DBExportTreeDescription(); + // + + // Test + bool DBStructureIsValid(); + + int DBQueryNumberOfChildren(tree::Node* n); + + // Insertion + bool DBInsert(tree::Node* alien_node) ; //, UpdateSummary& summary); + + // + std::string DBGetNodeId(tree::Node* node, const std::string& parent_id); + + tree::Node* GetNodeFromTypeId(int level, + const std::string& id); + + // + tree::Node* DBGetOrCreateNode(tree::Node* alien_node, + tree::Node* internal_parent, + std::string parentid, + std::string& created_id); + // UpdateSummary& summary); + + tree::Node* DBGetOrCreateParent(tree::Node* alien_node, + std::string& parent_id); + // UpdateSummary& summary); + + void DBRecursiveGetOrCreateNode(tree::Node* alien_node, + tree::Node* parent, + const std::string& parent_id); + // UpdateSummary& summary); + + + // + void DBRecursiveRemoveNode(tree::Node* node); + + + void BuildSQLFieldsValues(tree::Node* n, + std::string& str); + + + tree::Node* GetChildrenLike(tree::Node* internal_parent, + tree::Node* alien_node); + + private: + /// The DB + CppSQLite3DB* mDB; + /// The physical location associated to the DicomDatabase (directory, db file...) + std::string mFileName; + /// Is the DB writable ? + bool mWritable; + void SetWritable(bool w) { mWritable; } + bool GetWritable() const { return mWritable; } + + struct TypeId + { + int type; + std::string id; + bool operator< (const TypeId& o) const + { + return ((type TypeIdToNodeMapType; + TypeIdToNodeMapType mTypeIdToNodeMap; + }; + // EO class SQLiteTreeHandler + //======================================================================= + + +} // EO namespace creaImageIO + +// EOF +#endif + diff --git a/src2/creaImageIOSystem.h b/src2/creaImageIOSystem.h new file mode 100644 index 0000000..c98f7b8 --- /dev/null +++ b/src2/creaImageIOSystem.h @@ -0,0 +1,19 @@ +#ifndef __creaImageIOSystem_INCLUDED__ +#define __creaImageIOSystem_INCLUDED__ + + + +#if defined(_WIN32) + #ifdef CREAIMAGEIO_EXPORT_SYMBOLS + #define CREAIMAGEIO_EXPORT __declspec( dllexport ) +#else + #define CREAIMAGEIO_EXPORT __declspec( dllimport ) + #endif + #define CREAIMAGEIO_CDECL __cdecl +#else + #define CREAIMAGEIO_EXPORT + #define CREAIMAGEIO_CDECL +#endif // defined(_WIN32) + +#endif + diff --git a/src2/creaImageIOTree.cpp b/src2/creaImageIOTree.cpp new file mode 100644 index 0000000..f4f1bec --- /dev/null +++ b/src2/creaImageIOTree.cpp @@ -0,0 +1,22 @@ +#include + + +namespace creaImageIO +{ + namespace tree + { + + Tree::Tree() + : Node(0) + { + + } + + Tree::~Tree() + { + + } + + + } +} diff --git a/src2/creaImageIOTree.h b/src2/creaImageIOTree.h new file mode 100644 index 0000000..854693d --- /dev/null +++ b/src2/creaImageIOTree.h @@ -0,0 +1,78 @@ +#ifndef __creaImageIOTree_h_INCLUDED__ +#define __creaImageIOTree_h_INCLUDED__ + +#include + +namespace creaImageIO +{ + + namespace tree + { + + //===================================================================== + /// Abstract class to store user data on a tree + struct TreeData + { + TreeData() {} + virtual ~TreeData() {} + }; + //===================================================================== + + //===================================================================== + /// An attributed tree structure + class Tree : public Node + { + public: + /// Ctor + Tree(); + /// Virtual destructor + virtual ~Tree(); + + /// Returns the descriptor of the tree + + /// Returns the tree to which the node belongs + virtual Tree* GetTree() { return this; } + /// Returns the tree to which the node belongs + virtual const Tree* GetTree() const { return this; } + /// Returns the level of the node in the tree + virtual int GetLevel() const { return 0; } + + /// Returns the Descriptor of the tree (const) + const Descriptor& GetDescriptor() const { return mDescriptor; } + /// Returns the descriptor of the tree + Descriptor& GetDescriptor() { return mDescriptor; } + + /// Returns the number of levels of the tree + unsigned int GetNumberOfLevels() + { return GetDescriptor().GetNumberOfLevels(); } + + /// Returns the LevelDescriptor of a given level (const ref) + const LevelDescriptor& GetLevelDescriptor(int level) const + { return GetDescriptor().GetLevelDescriptor(level); } + /// Returns the LevelDescriptor of a given level (ref) + LevelDescriptor& GetLevelDescriptor(int level) + { return GetDescriptor().GetLevelDescriptor(level); } + + /// Returns the AttributeDescriptorList of a given level (const ref) + const LevelDescriptor::AttributeDescriptorListType& + GetAttributeDescriptorList(int level) const + { return GetDescriptor().GetAttributeDescriptorList(level); } + /// Returns the AttributeDescriptorList of a given level (ref) + LevelDescriptor::AttributeDescriptorListType& + GetAttributeDescriptorList(int level) + { return GetDescriptor().GetAttributeDescriptorList(level); } + + + private: + Descriptor mDescriptor; + + }; + // EO class Tree + //===================================================================== + + } // EO namespace tree + +} // EO namespace creaImageIO + +// EOF +#endif diff --git a/src2/creaImageIOTreeAttributeDescriptor.cpp b/src2/creaImageIOTreeAttributeDescriptor.cpp new file mode 100644 index 0000000..f01e260 --- /dev/null +++ b/src2/creaImageIOTreeAttributeDescriptor.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include + +namespace creaImageIO +{ + + namespace tree + { + + + // Ctor with name and flags + // If the name is of the form "1056|8948" (a pipe at 5th position) + // it is interpreted as a Dicom Tag + // The user name is retreived from dicom dictionnary + // and the group and elem are filled + AttributeDescriptor::AttributeDescriptor(const std::string& key, + unsigned int flags) + : mKey(key), mGroup(0), mElement(0), mFlags(flags) + { + // Is the key a Dicom tag ? + if ((key.size()==9)&&(key[4]=='|')) + { + // Decode group & elem + sscanf(key.c_str(),"%04x|%04x",&mGroup,&mElement); + // Retrieve the name from gdcm dict + GDCM_NAME_SPACE::DictEntry* entry = + GDCM_NAME_SPACE::Global::GetDicts() + ->GetDefaultPubDict()->GetEntry(mGroup,mElement); + mName = entry->GetName(); + } + else + { + mName = mKey; + } + } + + + + } // EO namespace tree + +} // EO namespace creaImageIO diff --git a/src2/creaImageIOTreeAttributeDescriptor.h b/src2/creaImageIOTreeAttributeDescriptor.h new file mode 100644 index 0000000..cc78838 --- /dev/null +++ b/src2/creaImageIOTreeAttributeDescriptor.h @@ -0,0 +1,77 @@ +#ifndef __creaImageIOTreeAttributeDescriptor_h_INCLUDED__ +#define __creaImageIOTreeAttributeDescriptor_h_INCLUDED__ + +#include +//#include + +namespace creaImageIO +{ + + namespace tree + { + //===================================================================== + /// Descriptor of an attribute of a node of a tree (name, dicom group/element) + class AttributeDescriptor + { + public: + /// Flags + /// The attribute is hidden (not visible to user) + static const unsigned int HIDDEN; + /// The attribute enters in unique identifier constitution (KEY) + static const unsigned int KEY; + /// The attribute enters in label constitution (for printing) + static const unsigned int LABEL; + + /// Default ctor + AttributeDescriptor() + : mKey(""), mName(""), mGroup(0), mElement(0), mFlags(0) + { + } + // Ctor with key and flags + // If the key is of the form "0020|000E" + // (Two hex short separated by a pipe at 5th position) + // it is interpreted as a Dicom Tag + // The user name is retreived from dicom dictionnary + // and the group and elem are filled + // Else the name is set to the key + AttributeDescriptor(const std::string& key, + unsigned int flags = 0); + /// Returns the name of the attribute + const std::string& GetName() const { return mName; } + /// Returns the DICOM group code of the attribute + unsigned short GetGroup() const { return mGroup; } + /// Returns the DICOM element code of the attribute + unsigned short GetElement() const { return mElement; } + /// Returns the flags of the attribute + unsigned int GetFlags() const { return mFlags; } + + private: + std::string mKey; + std::string mName; + unsigned short mGroup; + unsigned short mElement; + unsigned int mFlags; + }; + // EO class AttributeDescriptor + //===================================================================== + + + + } // EO namespace tree + +} // EO namespace creaImageIO + + +/* +//===================================================================== +inline std::ostream& operator<<(std::ostream& s, +const creaImageIO::tree::AttributeDescriptor& d) +{ +s << "[" << d.key << ":" << d.name << "]"; +return s; +} +//===================================================================== +*/ + + +#endif // #ifndef __creaImageIOTreeAttributeDescriptor_h_INCLUDED__ diff --git a/src2/creaImageIOTreeComparators.cpp b/src2/creaImageIOTreeComparators.cpp new file mode 100644 index 0000000..7d64496 --- /dev/null +++ b/src2/creaImageIOTreeComparators.cpp @@ -0,0 +1 @@ +#include diff --git a/src2/creaImageIOTreeComparators.h b/src2/creaImageIOTreeComparators.h new file mode 100644 index 0000000..d86bd24 --- /dev/null +++ b/src2/creaImageIOTreeComparators.h @@ -0,0 +1,212 @@ +#ifndef __creaImageIOTreeNodeComparators_h_INCLUDED__ +#define __creaImageIOTreeNodeComparators_h_INCLUDED__ + +#include +#include + +namespace creaImageIO +{ + + namespace tree + { + + + class Node; + + + + //===================================================================== + /// Abstract definition of a comparator of Node + struct Comparator + { + virtual ~Comparator() {} + virtual bool operator() (Node* const & x, Node* const & y) = 0; + }; + //===================================================================== + + + + //===================================================================== + /// Abstract Comparator whose order can be reversed + struct ComparatorWithOrder : public Comparator + { + ComparatorWithOrder(bool reverse_order = false) + : mReverseOrder(reverse_order) {} + virtual ~ComparatorWithOrder() {} + + + virtual bool compare(Node* const &, Node* const &) = 0; + + virtual bool operator() (Node* const & x, Node* const & y) + { + if (mReverseOrder) return this->compare(y,x); + return this->compare(x,y); + }; + + bool mReverseOrder; + }; + //===================================================================== + + + + //===================================================================== + /// A Comparator which stores a vector of Comparators and + /// which performs lexicographical comparison + class LexicographicalComparator : public Comparator + { + public: + LexicographicalComparator(const std::string& name) + : mName(name) {} + ~LexicographicalComparator() {} + + const std::string& GetName() const { return mName; } + void SetName(const std::string& s) { mName = s; } + void Clear() { mComparator.clear(); } + void DeleteComparators() + { + std::vector::iterator i; + for (i =mComparator.begin(); + i!=mComparator.end(); + ++i) + { + delete *i; + } + mComparator.clear(); + } + void Add(Comparator* c) { mComparator.push_back(c); } + + bool operator() (Node* const & x, Node * const & y); + + private: + std::string mName; + std::vector mComparator; + }; + //===================================================================== + + + + + //=================================================================== + /// Comparator which compares the values of a given Attribute of the Nodes which is decoded as an int value + struct IntComparator : + public ComparatorWithOrder + { + IntComparator(const std::string& key, + bool reverse_order) + : + ComparatorWithOrder(reverse_order), + mKey(key) + {} + virtual bool compare(Node* const & x, Node* const & y); + + private: + std::string mKey; + }; + //=================================================================== + + //=================================================================== + /// Comparator which compares the values of a given Attribute of the Nodes which is decoded as an float value + struct FloatComparator : + public ComparatorWithOrder + { + FloatComparator(const std::string& key, + bool reverse_order ) + : + ComparatorWithOrder(reverse_order), + mKey(key) + {} + + virtual bool compare(Node* const & x, Node* const & y); + + private: + std::string mKey; + }; + //=================================================================== + + //=================================================================== + /// Comparator which compares the values of a given Attribute of the Nodes which is decoded as a string value + struct StringComparator : + public ComparatorWithOrder + { + StringComparator(const std::string& key, + bool reverse_order ) + : + ComparatorWithOrder(reverse_order), + mKey(key) + {} + + virtual bool compare(Node* const & x, Node* const & y); + + private: + std::string mKey; + }; + //=================================================================== + + //=================================================================== +#define INT_FIELD_COMP(NAME,FIELD) \ + struct Node##NAME##Comparator : \ + public IntComparator \ + { \ + Node##NAME##Comparator(bool o = false) : \ + IntComparator(FIELD,o) \ + {} \ + } + //================================================================== + + //=================================================================== +#define FLOAT_FIELD_COMP(NAME,FIELD) \ + struct Node##NAME##Comparator : \ + public FloatComparator \ + { \ + Node##NAME##Comparator(bool o = false) : \ + FloatComparator(FIELD,o) \ + {} \ + } + //=================================================================== + + //=================================================================== +#define STRING_FIELD_COMP(NAME,FIELD) \ + struct Node##NAME##Comparator : \ + public StringComparator \ + { \ + Node##NAME##Comparator(bool o = false) : \ + StringComparator(FIELD,o) \ + {} \ + } + //=================================================================== + + + + //=================================================================== + // Patient comparators + STRING_FIELD_COMP(PatientName,"A0010_0010"); + STRING_FIELD_COMP(PatientSex, "A0010_0040"); + STRING_FIELD_COMP(PatientBirthday, "A0010_0030"); + //=================================================================== + + //=================================================================== + // Study comparators + STRING_FIELD_COMP(StudyDate,"A0008_0020"); + STRING_FIELD_COMP(StudyDescription,"A0008_1030"); + //=================================================================== + + //=================================================================== + // Series comparators + STRING_FIELD_COMP(Modality,"A0008_0060"); + STRING_FIELD_COMP(SeriesDescription,"A0008_103E"); + STRING_FIELD_COMP(SeriesDate,"A0008_0021"); + //=================================================================== + + //=================================================================== + // Image comparators + INT_FIELD_COMP(ImageNumber,"A0020_0013"); + FLOAT_FIELD_COMP(SliceLocation,"A0020_1041"); + STRING_FIELD_COMP(FullFileName,"FullFileName"); + //=================================================================== + } // namespace tree + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOComparators_h_INCLUDED__ diff --git a/src2/creaImageIOTreeDescriptor.cpp b/src2/creaImageIOTreeDescriptor.cpp new file mode 100644 index 0000000..fc4166f --- /dev/null +++ b/src2/creaImageIOTreeDescriptor.cpp @@ -0,0 +1,152 @@ +#include + + +namespace creaImageIO +{ + + namespace tree + { + + //================================================================== + /// The attribute is hidden (not visible to user) + const unsigned int AttributeDescriptor::HIDDEN = 1; + /// The attribute enters in unique identifier constitution (KEY) + const unsigned int AttributeDescriptor::KEY = 2; + const unsigned int AttributeDescriptor::LABEL = 4; + //================================================================== + + //================================================================== + Descriptor::Descriptor() + { + CreateLevel0Descriptor(); + } + //================================================================== + + //================================================================== + Descriptor::~Descriptor() + { + } + //================================================================== + + //================================================================== + void Descriptor::CreateLevel0Descriptor() + { + GetLevelDescriptorList().push_back(LevelDescriptor("Root")); + } + //================================================================== + + //================================================================== + /// Creates the default descriptor + void Descriptor::CreateDefault() + { + // clears the existing one + GetLevelDescriptorList().clear(); + + // Creates the level 0 descriptor + CreateLevel0Descriptor(); + // Creates the attribute "Name" + GetAttributeDescriptorList(0).push_back(AttributeDescriptor("Name")); + + // Patient level + GetLevelDescriptorList().push_back(LevelDescriptor("Patient")); + GetAttributeDescriptorList(1).push_back(AttributeDescriptor("0010|0010", // Patient name + AttributeDescriptor::LABEL)); + GetAttributeDescriptorList(1).push_back(AttributeDescriptor("0010|0040")); // Patient sex + GetAttributeDescriptorList(1).push_back(AttributeDescriptor("0010|0030")); // Patient birthday + GetAttributeDescriptorList(1).push_back(AttributeDescriptor("0010|0020", // Patient ID + AttributeDescriptor::KEY)); + + // Study-series level + GetLevelDescriptorList().push_back(LevelDescriptor("Series")); + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|0060", // Modality + AttributeDescriptor::LABEL)); + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|1030")); // Study Description + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|103E")); // Description + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|0080")); // Institution Name + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|0081")); // Institution Adress + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|1010")); // Station Name + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|1048")); // Physician of Record + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|1050")); // Performing Physician's Name + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0018|1030")); // Protocol Name + + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0020|0010")); // Study ID + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|0020")); // Study Date + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|0030")); // Study Time + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|0050")); // Study Accession Number + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|0005")); // Specific character set + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|0021")); // Series Date + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0008|0031")); // Series time + + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0020|000D", // Study Instance UID + AttributeDescriptor::KEY)); + GetAttributeDescriptorList(2).push_back(AttributeDescriptor("0020|000E", // Series Instance UID + AttributeDescriptor::KEY | + AttributeDescriptor::LABEL)); + + + // Image level + GetLevelDescriptorList().push_back(LevelDescriptor("Image")); + + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0020|0013")); // Image Number + + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0010")); // Rows + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0011")); // Columns + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0012")); // Planes + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0002")); // Sample per pixels + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0008")); // Number of Frames + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0004")); // Photometric Interpretation + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0103")); // Pixel Representation + + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0020|0032")); // Image Position Patient + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0020|0037")); // Image Orientation Patient + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0020|1041")); // Slice Location + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0006")); // Planar Configuration + + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0030")); // Pixel Spacing + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0100")); // AlocatedBits + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0028|0101")); // StoredBits + + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0008|0008")); // Image Type + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0008|0023")); // Content Date + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0008|0033")); // Content Time + + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0020|4000")); // Image Comments + + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0004|1500", // File Name + AttributeDescriptor::LABEL)); + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0004|1052")); // Rescale Intercept + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0004|1053")); // Rescale Slope + + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0050|0004")); // Calibration Image + + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0020|0052", // Frame Reference UID + AttributeDescriptor::KEY)); + GetAttributeDescriptorList(3).push_back(AttributeDescriptor("0008|0016")); // SOP Class UID + + + } + //================================================================== + + //================================================================== + /// Builds the key to value map of all the attributes of the tree + void Descriptor::BuildAttributeMap( std::map& map ) const + { + map.clear(); + LevelDescriptorListType::const_iterator l; + for (l = GetLevelDescriptorList().begin(); + l!= GetLevelDescriptorList().end(); + ++l) + { + LevelDescriptor::AttributeDescriptorListType::const_iterator a; + for (a = l->GetAttributeDescriptorList().begin(); + a!= l->GetAttributeDescriptorList().end(); + ++a) + { + map[a->GetName()]=""; + } + } + } + //================================================================== + + } +} diff --git a/src2/creaImageIOTreeDescriptor.h b/src2/creaImageIOTreeDescriptor.h new file mode 100644 index 0000000..e1812d3 --- /dev/null +++ b/src2/creaImageIOTreeDescriptor.h @@ -0,0 +1,74 @@ +#ifndef __creaImageIOTreeDescriptor_h_INCLUDED__ +#define __creaImageIOTreeDescriptor_h_INCLUDED__ + +#include +#include + +namespace creaImageIO +{ + + namespace tree + { + //===================================================================== + /// Descriptor of the structure a tree +/// (number of levels, descriptors of each level, ...). +/// Any tree has at least one level (level 0) of name "Root" +class Descriptor + { + public: + /// Ctor : creates the mandatory level 0 descriptor called "Root" + Descriptor(); + /// Destructor + ~Descriptor(); + + /// Loads from a xml description file + void LoadXML(const std::string& filename); + /// Creates the default descriptor + void CreateDefault(); + + /// Returns the number of levels of the tree + unsigned int GetNumberOfLevels() + { return mLevelDescriptorList.size(); } + + /// Returns the LevelDescriptor of a given level (const ref) + const LevelDescriptor& GetLevelDescriptor(int level) const + { return mLevelDescriptorList[level]; } + /// Returns the LevelDescriptor of a given level (ref) + LevelDescriptor& GetLevelDescriptor(int level) + { return mLevelDescriptorList[level]; } + + /// Returns the AttributeDescriptorList of a given level (const ref) + const LevelDescriptor::AttributeDescriptorListType& + GetAttributeDescriptorList(int level) const + { return mLevelDescriptorList[level].GetAttributeDescriptorList(); } + /// Returns the AttributeDescriptorList of a given level (ref) + LevelDescriptor::AttributeDescriptorListType& + GetAttributeDescriptorList(int level) + { return mLevelDescriptorList[level].GetAttributeDescriptorList(); } + + + /// Builds the key to value map of all the attributes of the tree + void BuildAttributeMap( std::map&) const; + + /// The type of LevelDescriptor container + typedef std::vector LevelDescriptorListType; + /// Returns the list of LevelDescriptor + LevelDescriptorListType& GetLevelDescriptorList() { return mLevelDescriptorList; } + /// Returns the list of tree levels (const) + const LevelDescriptorListType& GetLevelDescriptorList() const { return mLevelDescriptorList; } + + private: + LevelDescriptorListType mLevelDescriptorList; + /// Creates the mandatory level 0 descriptor called "Root" + /// (assumes the list is empty) + void CreateLevel0Descriptor(); + + }; + // EO class Descriptor + //===================================================================== + } // EO namespace tree + +} // EO namespace creaImageIO + +// EOF +#endif diff --git a/src2/creaImageIOTreeHandler.h b/src2/creaImageIOTreeHandler.h new file mode 100644 index 0000000..c88bbc9 --- /dev/null +++ b/src2/creaImageIOTreeHandler.h @@ -0,0 +1,114 @@ +#ifndef __creaImageIOTreeHandler_h_INCLUDED__ +#define __creaImageIOTreeHandler_h_INCLUDED__ + +#include + +namespace creaImageIO +{ + + + //======================================================================= + /// Abstract class which 'handles' a Tree structure + class TreeHandler + { + public: + ///==================================================================== + /// Ctor + TreeHandler() {} + /// Virtual dtor + virtual ~TreeHandler() {} + ///==================================================================== + + ///==================================================================== + /// Returns the Tree handled + tree::Tree& GetTree() { return mTree; } + /// Returns the Tree handled (const) + const tree::Tree& GetTree() const { return mTree; } + ///==================================================================== + + ///==================================================================== + /// QUERY METHODS + /// Is the 'source' readable ? + virtual bool IsReadable() { return false; } + /// Is the 'source' writable ? + virtual bool IsWritable() { return false; } + ///==================================================================== + + + ///==================================================================== + /// INITIALIZATION / FINALIZATION + ///==================================================================== + + ///==================================================================== + /// Opens an existing 'source' + /// Default mode is read only + /// If IsWritable and writable==true then opens in read/write mode + virtual bool Open(bool writable = false) { return false; } + /// Closes the 'source' + virtual bool Close() { return false; } + /// Creates a new 'source' + /// Default mode is read only + /// If IsWritable and writable==true then opens in read/write mode + virtual bool Create(bool writable = false) { return false; } + /// Destroys the 'source' + virtual bool Destroy() { return false; } + ///==================================================================== + + + ///==================================================================== + // READ METHODS + ///==================================================================== + + + ///==================================================================== + /// Returns the number of children of the Node *WITHOUT LOADING THEM* + /// REM : The Tree itself is a Node and asking for its number of + /// children returns the number of children of level 1. + virtual unsigned int GetNumberOfChildren(tree::Node* n) { return 0; } + ///==================================================================== + + ///==================================================================== + /// Recursively loads the children of node 'parent' until maxlevel + /// is reached. + /// If parent == NULL or parent == tree then starts with the 'children' of + /// the tree itself. + /// Returns the total number of children loaded. + virtual int LoadChildren(tree::Node* parent, int maxlevel) + { return 0; } + ///==================================================================== + + ///==================================================================== + /// Unloads the Node and its descendants + /// WITHOUT altering the source, e.g. the database + virtual void UnLoad(tree::Node* n) { return; } + ///==================================================================== + + + ///==================================================================== + /// WRITE METHODS : WORK ONLY IN WRITE MODE + ///==================================================================== + /// Adds a branch in the tree with the attributes provided + /// returns the Level in the tree where the branch was connected + /// (-1 for error, 0 for top level, etc. ) + /// Of course the branch is loaded on exit + virtual int AddBranch + ( const std::map& attr ) { return -1; } + /// Removes the node and its descendants + bool Remove(tree::Node*) { return false; } + ///==================================================================== + + + private: + /// The handled tree + tree::Tree mTree; + + }; + // EO class TreeHandler + //======================================================================= + + +} // EO namespace creaImageIO + +// EOF +#endif + diff --git a/src2/creaImageIOTreeLevelDescriptor.cpp b/src2/creaImageIOTreeLevelDescriptor.cpp new file mode 100644 index 0000000..7cdd9fd --- /dev/null +++ b/src2/creaImageIOTreeLevelDescriptor.cpp @@ -0,0 +1,10 @@ +#include + +namespace creaImageIO +{ + namespace tree + { + + } +} + diff --git a/src2/creaImageIOTreeLevelDescriptor.h b/src2/creaImageIOTreeLevelDescriptor.h new file mode 100644 index 0000000..95b284b --- /dev/null +++ b/src2/creaImageIOTreeLevelDescriptor.h @@ -0,0 +1,52 @@ +#ifndef __creaImageIOTreeLevelDescriptor_h_INCLUDED__ +#define __creaImageIOTreeLevelDescriptor_h_INCLUDED__ + +#include +#include + +namespace creaImageIO +{ + + namespace tree + { + //===================================================================== + /// Descriptor of a level of a tree (name, attributes, ...) + class LevelDescriptor + { + public: + /// Ctor with name + LevelDescriptor(const std::string& name) : mName(name) {} + /// Destructor + ~LevelDescriptor() {} + + /// Returns the name of the level + const std::string& GetName() { return mName; } + + /// Returns the number of attributes of the level + unsigned int GetNumberOfAttributes() + { return mAttributeDescriptorList.size(); } + + /// The type of attribute container + typedef std::vector AttributeDescriptorListType; + /// Returns the list of AttributeDescriptor + AttributeDescriptorListType& GetAttributeDescriptorList() + { return mAttributeDescriptorList; } + /// Returns the list of AttributeDescriptor (const) + const AttributeDescriptorListType& GetAttributeDescriptorList() const + { return mAttributeDescriptorList; } + + private: + std::string mName; + AttributeDescriptorListType mAttributeDescriptorList; + + + }; + // EO class LevelDescriptor + //===================================================================== + }// EO namespace tree + + +} // EO namespace creaImageIO + +// EOF +#endif diff --git a/src2/creaImageIOTreeNode.cpp b/src2/creaImageIOTreeNode.cpp new file mode 100644 index 0000000..8f03924 --- /dev/null +++ b/src2/creaImageIOTreeNode.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include + +namespace creaImageIO +{ + namespace tree + { + + //============================================================= + /// Ctor with parent + Node::Node(Node* parent) + : mParent(parent), + mData(0), + mChildrenLoaded(false) + { + if (parent) + { + // Insert into parent's children list + parent->GetChildrenList().push_back(this); + // Initialize attributes + LevelDescriptor::AttributeDescriptorListType::const_iterator a; + for (a = GetTree()->GetAttributeDescriptorList(GetLevel()).begin(); + a!= GetTree()->GetAttributeDescriptorList(GetLevel()).end(); + ++a) + { + UnsafeSetAttribute( a->GetName(), "" ); + } + } + } + //============================================================= + + + + //============================================================= + Node::~Node() + { + ChildrenListType::iterator i; + for (i=GetChildrenList().begin(); i!=GetChildrenList().end(); i++) + { + delete *i; + } + if (mData) + { + delete mData; + mData = 0; + } + } + //============================================================= + + //============================================================= + void Node::RemoveChildrenFromList(Node* node) + { + ChildrenListType::iterator i = find(GetChildrenList().begin(), + GetChildrenList().end(), + node); + if (i != GetChildrenList().end()) + { + GetChildrenList().erase(i); + } + } + //============================================================= + + //============================================================= + const std::string& Node::GetAttribute(const std::string& k) const + { + // std::cout << "this = "<<(void*)this<second; + } + //============================================================= + + //============================================================= + void Node::SetAttribute(const std::string& k, + const std::string& v) + { + AttributeMapType::iterator i = mAttributeMap.find(k); + if (i==mAttributeMap.end()) + { + std::cout<<"[Gimmick!] Node::SetAttribute : no attribute with name '" + <second = v; + } + //============================================================= + + + } + +} + diff --git a/src2/creaImageIOTreeNode.h b/src2/creaImageIOTreeNode.h new file mode 100644 index 0000000..07b46cf --- /dev/null +++ b/src2/creaImageIOTreeNode.h @@ -0,0 +1,137 @@ +#ifndef __creaImageIOTreeNode_h_INCLUDED__ +#define __creaImageIOTreeNode_h_INCLUDED__ + +#include +#include +#include +#include + +namespace creaImageIO +{ + + namespace tree + { + //===================================================================== + /// Forward declaration of Tree + class Tree; + //===================================================================== + + //===================================================================== + /// Abstract class to store user data on a tree node + struct NodeData + { + NodeData() {} + virtual ~NodeData() {} + }; + //===================================================================== + + + //===================================================================== + /// Node of an attributed tree structure + class Node + { + public: + /// Ctor with parent + Node(Node* parent); + /// Virtual destructor + virtual ~Node(); + + + /// Returns the level descriptor of the node + const LevelDescriptor& GetLevelDescriptor() const; + + + /// Returns the tree to which the node belongs + virtual Tree* GetTree() { return mParent->GetTree(); } + /// Returns the tree to which the node belongs + virtual const Tree* GetTree() const { return mParent->GetTree(); } + /// Returns the level of the node in the tree + virtual int GetLevel() const { return mParent->GetLevel()+1; } + + /// Returns the parent of the node + Node* GetParent() const { return mParent; } + + /// Returns the number of children of the node. + /// Warning : if the children are not loaded then might return 0 + /// even if the node has children ! + /// see TreeHandler::GetNumberOfChildren + unsigned int GetNumberOfChildren() const { return mChildren.size(); } + + /// Returns true iff the node's children are loaded + bool GetChildrenLoaded() const { return mChildrenLoaded; } + + /// Sets the node's children + void SetChildrenLoaded(bool l) { mChildrenLoaded = l; } + + /// The type of children container + typedef std::vector ChildrenListType; + /// Returns the list of children + ChildrenListType& GetChildrenList() { return mChildren; } + /// Returns the list of children (const) + const ChildrenListType& GetChildrenList() const { return mChildren; } + + /// Remove the given children from the children list + void RemoveChildrenFromList(Node*); + + typedef std::map AttributeMapType; + + AttributeMapType& GetAttributeMap() { return mAttributeMap; } + const AttributeMapType& GetAttributeMap() const { return mAttributeMap; } + const std::string& GetAttribute(const std::string& k) const; + const std::string& GetCleanAttribute(const std::string& k) const; + const std::string& UnsafeGetAttribute(const std::string& k) const + { return mAttributeMap.find(k)->second; } + void SetAttribute(const std::string& k, const std::string& v); + void UnsafeSetAttribute(const std::string& k, const std::string& v) + { mAttributeMap[k] = v; } + + const AttributeDescriptor& GetAttributeDescriptor(const std::string& k) + const; + // { return GetTypeDescription().GetFieldDescription(k); } + + + + /// Returns the node data casted into the type T + template T GetData() const + { if (mData!=0) return dynamic_cast(mData); return 0; } + + /// Sets the node data. Deletes existing data if any. + void SetData(NodeData* d) { if (mData) delete mData; mData = d; } + + /// Sorts the children of the node + void SortChildren(const LexicographicalComparator&); + + /* + virtual void Print() const; + std::string GetLabel() const; + int ImageGetRows() const; + int ImageGetColumns() const; + int ImageGetFrames() const; + const std::string& ImageGetFullFileName() const { return UnsafeGetAttribute("FullFileName"); } + */ + + private: + /// The parent of the node + Node* mParent; + /// The list of children + ChildrenListType mChildren; + /// The map of attributes + AttributeMapType mAttributeMap; + /// User data + NodeData* mData; + /// Are the children loaded ? + bool mChildrenLoaded; + /// The number of children + // int mNumberOfChildren; + + }; // class Node + //===================================================================== + + } // namespace tree + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOTreeNode_h_INCLUDED__ -- 2.45.1