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)
-SUBDIRS(TestWxGimmickDialog)
+IF (BUILD_V2)
+ SUBDIRS(gimmick)
+ELSE (BUILD_V2)
+ SUBDIRS(TestWxGimmickDialog)
+ENDIF (BUILD_V2)
+
+
+
+
--- /dev/null
+
+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 )
--- /dev/null
+#include <creaImageIOGimmick.h>
+
+int main(int argc, char* argv[])
+{
+ creaImageIO::Gimmick g;
+ if (!g.Initialize()) return 1;
+ if (!g.Finalize()) return 1;
+ return 0;
+}
creaImageIOWxGimmickDialog
-
# creaImageIOWxDicomDatabaseTreeView
# creaImageIOWxDicomDatabaseTreeViewSettings
# creaImageIOWxDicomNodeFieldsView
//=============================================================
void DicomNode::Print() const
{
- for (int i=0;i<GetType()+1;++i) std::cout << " ";
+ for (int ii=0;ii<GetType()+1;++ii) std::cout << " ";
std::cout << "-> "<<GetLabel() << std::endl;
ChildrenListType::const_iterator i;
for (i=GetChildrenList().begin(); i!=GetChildrenList().end(); i++)
{
if ( x->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);
}
//===================================================================
PopUp_Settings = 501,
PopUp_About = 502,
PopUp_User = WxGimmick::UserMenuFirstId,
+ PopUp_SaveAs = 701,
+ PopUp_AddToFavorites = 702
};
//================================================================
{
wxBusyCursor busy;
// std::cout << "WxGimmick : Reading config"<<std::endl;
+ LoadOrCreateFavoritesDatabase();
// std::cout <<
creaMessage("Gimmick!",1,"Gimmick! : ==> Loading collections from '"<<mDatabaseListFile<<"'"<<std::endl);
// <<"'"<<std::endl;
+
std::ifstream s;
s.open(mDatabaseListFile.c_str());
if (s.good())
}
//================================================================
+ //================================================================
+ void WxGimmick::LoadOrCreateFavoritesDatabase()
+ {
+ // TODO
+ }
+ //================================================================
+
/*
//================================================================
void WxGimmick::OnClose(wxCloseEvent& event)
public MultiThreadImageReaderUser
{
public:
-
+ /// Ctor
WxGimmick(wxWindow *parent, const wxWindowID id,
const wxPoint& pos, const wxSize& size,
int image_type = GIMMICK_3D_IMAGE_SELECTION,
int number_of_threads = 0);
-
+ /// Dtor
virtual ~WxGimmick();
+ /// Returns the size of the current selection
int GetSelectionSize() { return mTreeListCtrl->GetSelectionSize(); }
+ /// Returns true if there is a valid selection
bool IsSelectionValid();
+ /// Returns the vector of full filenames of selected images
void GetSelectedFiles(std::vector<std::string>&);
+ /// Returns the vector of images corresponding to selection
void GetSelectedImages(std::vector<vtkImageData*>&);
+ /// Returns the vector of DicomNode corresponding to selection
void GetSelectedDicomNodes(std::vector<DicomNode*>&);
+ /// Returns the vector of wxTreeItemId corresponding to selection
void GetSelectedItems(std::vector<wxTreeItemId>&);
+
+ /// 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<DicomDatabase*> 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);
int mCurrentSelectionImageSize[4];
DicomDatabaseListType mDicomDatabaseList;
+ DicomDatabase* mFavoriteDatabase;
wxTreeListCtrl* mTreeListCtrl;
wxTreeItemId mTreeRootId;
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);
--- /dev/null
+INCLUDE(${crea_USE_FILE})
+
--- /dev/null
+# We have to find crea
+FIND_PACKAGE(crea REQUIRED)
\ No newline at end of file
--- /dev/null
+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
+ )
--- /dev/null
+////////////////////////////////////////////////////////////////////////////////\r
+// CppSQLite3 - A C++ wrapper around the SQLite3 embedded database library.\r
+//\r
+// Copyright (c) 2004 Rob Groves. All Rights Reserved. rob.groves@btinternet.com\r
+// \r
+// Permission to use, copy, modify, and distribute this software and its\r
+// documentation for any purpose, without fee, and without a written\r
+// agreement, is hereby granted, provided that the above copyright notice, \r
+// this paragraph and the following two paragraphs appear in all copies, \r
+// modifications, and distributions.\r
+//\r
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,\r
+// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST\r
+// PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,\r
+// EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+//\r
+// THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT\r
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\r
+// PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF\r
+// ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". THE AUTHOR HAS NO OBLIGATION\r
+// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\r
+//\r
+// V3.0 03/08/2004 -Initial Version for sqlite3\r
+//\r
+// V3.1 16/09/2004 -Implemented getXXXXField using sqlite3 functions\r
+// -Added CppSQLiteDB3::tableExists()\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "CppSQLite3.h"\r
+#include <cstdlib>\r
+\r
+\r
+// Named constant for passing to CppSQLite3Exception when passing it a string\r
+// that cannot be deleted.\r
+static const bool DONT_DELETE_MSG=false;\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Prototypes for SQLite functions not included in SQLite DLL, but copied below\r
+// from SQLite encode.c\r
+////////////////////////////////////////////////////////////////////////////////\r
+int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out);\r
+int sqlite3_decode_binary(const unsigned char *in, unsigned char *out);\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+CppSQLite3Exception::CppSQLite3Exception(const int nErrCode,\r
+ const char* szErrMess,\r
+ bool bDeleteMsg/*=true*/) :\r
+ mnErrCode(nErrCode)\r
+{\r
+ mpszErrMess = sqlite3_mprintf("%s[%d]: %s",\r
+ errorCodeAsString(nErrCode),\r
+ nErrCode,\r
+ szErrMess ? szErrMess : "");\r
+ /*\r
+ if (bDeleteMsg && szErrMess)\r
+ {\r
+ sqlite3_free(szErrMess);\r
+ }\r
+ */\r
+}\r
+\r
+CppSQLite3Exception::CppSQLite3Exception(const int nErrCode,\r
+ char* szErrMess,\r
+ bool bDeleteMsg/*=true*/) :\r
+ mnErrCode(nErrCode)\r
+{\r
+ mpszErrMess = sqlite3_mprintf("%s[%d]: %s",\r
+ errorCodeAsString(nErrCode),\r
+ nErrCode,\r
+ szErrMess ? szErrMess : "");\r
+ \r
+ if (bDeleteMsg && szErrMess)\r
+ {\r
+ sqlite3_free(szErrMess);\r
+ }\r
+}\r
+ \r
+CppSQLite3Exception::CppSQLite3Exception(const CppSQLite3Exception& e) :\r
+ mnErrCode(e.mnErrCode)\r
+{\r
+ mpszErrMess = 0;\r
+ if (e.mpszErrMess)\r
+ {\r
+ mpszErrMess = sqlite3_mprintf("%s", e.mpszErrMess);\r
+ }\r
+}\r
+\r
+\r
+const char* CppSQLite3Exception::errorCodeAsString(int nErrCode)\r
+{\r
+ switch (nErrCode)\r
+ {\r
+ case SQLITE_OK : return "SQLITE_OK";\r
+ case SQLITE_ERROR : return "SQLITE_ERROR";\r
+ case SQLITE_INTERNAL : return "SQLITE_INTERNAL";\r
+ case SQLITE_PERM : return "SQLITE_PERM";\r
+ case SQLITE_ABORT : return "SQLITE_ABORT";\r
+ case SQLITE_BUSY : return "SQLITE_BUSY";\r
+ case SQLITE_LOCKED : return "SQLITE_LOCKED";\r
+ case SQLITE_NOMEM : return "SQLITE_NOMEM";\r
+ case SQLITE_READONLY : return "SQLITE_READONLY";\r
+ case SQLITE_INTERRUPT : return "SQLITE_INTERRUPT";\r
+ case SQLITE_IOERR : return "SQLITE_IOERR";\r
+ case SQLITE_CORRUPT : return "SQLITE_CORRUPT";\r
+ case SQLITE_NOTFOUND : return "SQLITE_NOTFOUND";\r
+ case SQLITE_FULL : return "SQLITE_FULL";\r
+ case SQLITE_CANTOPEN : return "SQLITE_CANTOPEN";\r
+ case SQLITE_PROTOCOL : return "SQLITE_PROTOCOL";\r
+ case SQLITE_EMPTY : return "SQLITE_EMPTY";\r
+ case SQLITE_SCHEMA : return "SQLITE_SCHEMA";\r
+ case SQLITE_TOOBIG : return "SQLITE_TOOBIG";\r
+ case SQLITE_CONSTRAINT : return "SQLITE_CONSTRAINT";\r
+ case SQLITE_MISMATCH : return "SQLITE_MISMATCH";\r
+ case SQLITE_MISUSE : return "SQLITE_MISUSE";\r
+ case SQLITE_NOLFS : return "SQLITE_NOLFS";\r
+ case SQLITE_AUTH : return "SQLITE_AUTH";\r
+ case SQLITE_FORMAT : return "SQLITE_FORMAT";\r
+ case SQLITE_RANGE : return "SQLITE_RANGE";\r
+ case SQLITE_ROW : return "SQLITE_ROW";\r
+ case SQLITE_DONE : return "SQLITE_DONE";\r
+ case CPPSQLITE_ERROR : return "CPPSQLITE_ERROR";\r
+ default: return "UNKNOWN_ERROR";\r
+ }\r
+}\r
+\r
+\r
+CppSQLite3Exception::~CppSQLite3Exception()\r
+{\r
+ if (mpszErrMess)\r
+ {\r
+ sqlite3_free(mpszErrMess);\r
+ mpszErrMess = 0;\r
+ }\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+CppSQLite3Buffer::CppSQLite3Buffer()\r
+{\r
+ mpBuf = 0;\r
+}\r
+\r
+\r
+CppSQLite3Buffer::~CppSQLite3Buffer()\r
+{\r
+ clear();\r
+}\r
+\r
+\r
+void CppSQLite3Buffer::clear()\r
+{\r
+ if (mpBuf)\r
+ {\r
+ sqlite3_free(mpBuf);\r
+ mpBuf = 0;\r
+ }\r
+\r
+}\r
+\r
+\r
+const char* CppSQLite3Buffer::format(const char* szFormat, ...)\r
+{\r
+ clear();\r
+ va_list va;\r
+ va_start(va, szFormat);\r
+ mpBuf = sqlite3_vmprintf(szFormat, va);\r
+ va_end(va);\r
+ return mpBuf;\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+CppSQLite3Binary::CppSQLite3Binary() :\r
+ mpBuf(0),\r
+ mnBinaryLen(0),\r
+ mnBufferLen(0),\r
+ mnEncodedLen(0),\r
+ mbEncoded(false)\r
+{\r
+}\r
+\r
+\r
+CppSQLite3Binary::~CppSQLite3Binary()\r
+{\r
+ clear();\r
+}\r
+\r
+\r
+void CppSQLite3Binary::setBinary(const unsigned char* pBuf, int nLen)\r
+{\r
+ mpBuf = allocBuffer(nLen);\r
+ memcpy(mpBuf, pBuf, nLen);\r
+}\r
+\r
+\r
+void CppSQLite3Binary::setEncoded(const unsigned char* pBuf)\r
+{\r
+ clear();\r
+\r
+ mnEncodedLen = strlen((const char*)pBuf);\r
+ mnBufferLen = mnEncodedLen + 1; // Allow for NULL terminator\r
+\r
+ mpBuf = (unsigned char*)malloc(mnBufferLen);\r
+\r
+ if (!mpBuf)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Cannot allocate memory",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ memcpy(mpBuf, pBuf, mnBufferLen);\r
+ mbEncoded = true;\r
+}\r
+\r
+\r
+const unsigned char* CppSQLite3Binary::getEncoded()\r
+{\r
+ if (!mbEncoded)\r
+ {\r
+ unsigned char* ptmp = (unsigned char*)malloc(mnBinaryLen);\r
+ memcpy(ptmp, mpBuf, mnBinaryLen);\r
+ mnEncodedLen = sqlite3_encode_binary(ptmp, mnBinaryLen, mpBuf);\r
+ free(ptmp);\r
+ mbEncoded = true;\r
+ }\r
+\r
+ return mpBuf;\r
+}\r
+\r
+\r
+const unsigned char* CppSQLite3Binary::getBinary()\r
+{\r
+ if (mbEncoded)\r
+ {\r
+ // in/out buffers can be the same\r
+ mnBinaryLen = sqlite3_decode_binary(mpBuf, mpBuf);\r
+\r
+ if (mnBinaryLen == -1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Cannot decode binary",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ mbEncoded = false;\r
+ }\r
+\r
+ return mpBuf;\r
+}\r
+\r
+\r
+int CppSQLite3Binary::getBinaryLength()\r
+{\r
+ getBinary();\r
+ return mnBinaryLen;\r
+}\r
+\r
+\r
+unsigned char* CppSQLite3Binary::allocBuffer(int nLen)\r
+{\r
+ clear();\r
+\r
+ // Allow extra space for encoded binary as per comments in\r
+ // SQLite encode.c See bottom of this file for implementation\r
+ // of SQLite functions use 3 instead of 2 just to be sure ;-)\r
+ mnBinaryLen = nLen;\r
+ mnBufferLen = 3 + (257*nLen)/254;\r
+\r
+ mpBuf = (unsigned char*)malloc(mnBufferLen);\r
+\r
+ if (!mpBuf)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Cannot allocate memory",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ mbEncoded = false;\r
+\r
+ return mpBuf;\r
+}\r
+\r
+\r
+void CppSQLite3Binary::clear()\r
+{\r
+ if (mpBuf)\r
+ {\r
+ mnBinaryLen = 0;\r
+ mnBufferLen = 0;\r
+ free(mpBuf);\r
+ mpBuf = 0;\r
+ }\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+CppSQLite3Query::CppSQLite3Query()\r
+{\r
+ mpVM = 0;\r
+ mbEof = true;\r
+ mnCols = 0;\r
+ mbOwnVM = false;\r
+}\r
+\r
+\r
+CppSQLite3Query::CppSQLite3Query(const CppSQLite3Query& rQuery)\r
+{\r
+ mpVM = rQuery.mpVM;\r
+ // Only one object can own the VM\r
+ const_cast<CppSQLite3Query&>(rQuery).mpVM = 0;\r
+ mbEof = rQuery.mbEof;\r
+ mnCols = rQuery.mnCols;\r
+ mbOwnVM = rQuery.mbOwnVM;\r
+}\r
+\r
+\r
+CppSQLite3Query::CppSQLite3Query(sqlite3* pDB,\r
+ sqlite3_stmt* pVM,\r
+ bool bEof,\r
+ bool bOwnVM/*=true*/)\r
+{\r
+ mpDB = pDB;\r
+ mpVM = pVM;\r
+ mbEof = bEof;\r
+ mnCols = sqlite3_column_count(mpVM);\r
+ mbOwnVM = bOwnVM;\r
+}\r
+\r
+\r
+CppSQLite3Query::~CppSQLite3Query()\r
+{\r
+ try\r
+ {\r
+ finalize();\r
+ }\r
+ catch (...)\r
+ {\r
+ }\r
+}\r
+\r
+\r
+CppSQLite3Query& CppSQLite3Query::operator=(const CppSQLite3Query& rQuery)\r
+{\r
+ try\r
+ {\r
+ finalize();\r
+ }\r
+ catch (...)\r
+ {\r
+ }\r
+ mpVM = rQuery.mpVM;\r
+ // Only one object can own the VM\r
+ const_cast<CppSQLite3Query&>(rQuery).mpVM = 0;\r
+ mbEof = rQuery.mbEof;\r
+ mnCols = rQuery.mnCols;\r
+ mbOwnVM = rQuery.mbOwnVM;\r
+ return *this;\r
+}\r
+\r
+\r
+int CppSQLite3Query::numFields()\r
+{\r
+ checkVM();\r
+ return mnCols;\r
+}\r
+\r
+\r
+const char* CppSQLite3Query::fieldValue(int nField)\r
+{\r
+ checkVM();\r
+\r
+ if (nField < 0 || nField > mnCols-1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid field index requested",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ return (const char*)sqlite3_column_text(mpVM, nField);\r
+}\r
+\r
+\r
+const char* CppSQLite3Query::fieldValue(const char* szField)\r
+{\r
+ int nField = fieldIndex(szField);\r
+ return (const char*)sqlite3_column_text(mpVM, nField);\r
+}\r
+\r
+\r
+int CppSQLite3Query::getIntField(int nField, int nNullValue/*=0*/)\r
+{\r
+ if (fieldDataType(nField) == SQLITE_NULL)\r
+ {\r
+ return nNullValue;\r
+ }\r
+ else\r
+ {\r
+ return sqlite3_column_int(mpVM, nField);\r
+ }\r
+}\r
+\r
+\r
+int CppSQLite3Query::getIntField(const char* szField, int nNullValue/*=0*/)\r
+{\r
+ int nField = fieldIndex(szField);\r
+ return getIntField(nField, nNullValue);\r
+}\r
+\r
+\r
+double CppSQLite3Query::getFloatField(int nField, double fNullValue/*=0.0*/)\r
+{\r
+ if (fieldDataType(nField) == SQLITE_NULL)\r
+ {\r
+ return fNullValue;\r
+ }\r
+ else\r
+ {\r
+ return sqlite3_column_double(mpVM, nField);\r
+ }\r
+}\r
+\r
+\r
+double CppSQLite3Query::getFloatField(const char* szField, double fNullValue/*=0.0*/)\r
+{\r
+ int nField = fieldIndex(szField);\r
+ return getFloatField(nField, fNullValue);\r
+}\r
+\r
+\r
+const char* CppSQLite3Query::getStringField(int nField, const char* szNullValue/*=""*/)\r
+{\r
+ if (fieldDataType(nField) == SQLITE_NULL)\r
+ {\r
+ return szNullValue;\r
+ }\r
+ else\r
+ {\r
+ return (const char*)sqlite3_column_text(mpVM, nField);\r
+ }\r
+}\r
+\r
+\r
+const char* CppSQLite3Query::getStringField(const char* szField, const char* szNullValue/*=""*/)\r
+{\r
+ int nField = fieldIndex(szField);\r
+ return getStringField(nField, szNullValue);\r
+}\r
+\r
+\r
+const unsigned char* CppSQLite3Query::getBlobField(int nField, int& nLen)\r
+{\r
+ checkVM();\r
+\r
+ if (nField < 0 || nField > mnCols-1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid field index requested",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ nLen = sqlite3_column_bytes(mpVM, nField);\r
+ return (const unsigned char*)sqlite3_column_blob(mpVM, nField);\r
+}\r
+\r
+\r
+const unsigned char* CppSQLite3Query::getBlobField(const char* szField, int& nLen)\r
+{\r
+ int nField = fieldIndex(szField);\r
+ return getBlobField(nField, nLen);\r
+}\r
+\r
+\r
+bool CppSQLite3Query::fieldIsNull(int nField)\r
+{\r
+ return (fieldDataType(nField) == SQLITE_NULL);\r
+}\r
+\r
+\r
+bool CppSQLite3Query::fieldIsNull(const char* szField)\r
+{\r
+ int nField = fieldIndex(szField);\r
+ return (fieldDataType(nField) == SQLITE_NULL);\r
+}\r
+\r
+\r
+int CppSQLite3Query::fieldIndex(const char* szField)\r
+{\r
+ checkVM();\r
+\r
+ if (szField)\r
+ {\r
+ for (int nField = 0; nField < mnCols; nField++)\r
+ {\r
+ const char* szTemp = sqlite3_column_name(mpVM, nField);\r
+\r
+ if (strcmp(szField, szTemp) == 0)\r
+ {\r
+ return nField;\r
+ }\r
+ }\r
+ }\r
+\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid field name requested",\r
+ DONT_DELETE_MSG);\r
+}\r
+\r
+\r
+const char* CppSQLite3Query::fieldName(int nCol)\r
+{\r
+ checkVM();\r
+\r
+ if (nCol < 0 || nCol > mnCols-1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid field index requested",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ return sqlite3_column_name(mpVM, nCol);\r
+}\r
+\r
+\r
+const char* CppSQLite3Query::fieldDeclType(int nCol)\r
+{\r
+ checkVM();\r
+\r
+ if (nCol < 0 || nCol > mnCols-1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid field index requested",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ return sqlite3_column_decltype(mpVM, nCol);\r
+}\r
+\r
+\r
+int CppSQLite3Query::fieldDataType(int nCol)\r
+{\r
+ checkVM();\r
+\r
+ if (nCol < 0 || nCol > mnCols-1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid field index requested",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ return sqlite3_column_type(mpVM, nCol);\r
+}\r
+\r
+\r
+bool CppSQLite3Query::eof()\r
+{\r
+ checkVM();\r
+ return mbEof;\r
+}\r
+\r
+\r
+void CppSQLite3Query::nextRow()\r
+{\r
+ checkVM();\r
+\r
+ int nRet = sqlite3_step(mpVM);\r
+\r
+ if (nRet == SQLITE_DONE)\r
+ {\r
+ // no rows\r
+ mbEof = true;\r
+ }\r
+ else if (nRet == SQLITE_ROW)\r
+ {\r
+ // more rows, nothing to do\r
+ }\r
+ else\r
+ {\r
+ nRet = sqlite3_finalize(mpVM);\r
+ mpVM = 0;\r
+ const char* szError = sqlite3_errmsg(mpDB);\r
+ throw CppSQLite3Exception(nRet,\r
+ (char*)szError,\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Query::finalize()\r
+{\r
+ if (mpVM && mbOwnVM)\r
+ {\r
+ int nRet = sqlite3_finalize(mpVM);\r
+ mpVM = 0;\r
+ if (nRet != SQLITE_OK)\r
+ {\r
+ const char* szError = sqlite3_errmsg(mpDB);\r
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);\r
+ }\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Query::checkVM()\r
+{\r
+ if (mpVM == 0)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Null Virtual Machine pointer",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+CppSQLite3Table::CppSQLite3Table()\r
+{\r
+ mpaszResults = 0;\r
+ mnRows = 0;\r
+ mnCols = 0;\r
+ mnCurrentRow = 0;\r
+}\r
+\r
+\r
+CppSQLite3Table::CppSQLite3Table(const CppSQLite3Table& rTable)\r
+{\r
+ mpaszResults = rTable.mpaszResults;\r
+ // Only one object can own the results\r
+ const_cast<CppSQLite3Table&>(rTable).mpaszResults = 0;\r
+ mnRows = rTable.mnRows;\r
+ mnCols = rTable.mnCols;\r
+ mnCurrentRow = rTable.mnCurrentRow;\r
+}\r
+\r
+\r
+CppSQLite3Table::CppSQLite3Table(char** paszResults, int nRows, int nCols)\r
+{\r
+ mpaszResults = paszResults;\r
+ mnRows = nRows;\r
+ mnCols = nCols;\r
+ mnCurrentRow = 0;\r
+}\r
+\r
+\r
+CppSQLite3Table::~CppSQLite3Table()\r
+{\r
+ try\r
+ {\r
+ finalize();\r
+ }\r
+ catch (...)\r
+ {\r
+ }\r
+}\r
+\r
+\r
+CppSQLite3Table& CppSQLite3Table::operator=(const CppSQLite3Table& rTable)\r
+{\r
+ try\r
+ {\r
+ finalize();\r
+ }\r
+ catch (...)\r
+ {\r
+ }\r
+ mpaszResults = rTable.mpaszResults;\r
+ // Only one object can own the results\r
+ const_cast<CppSQLite3Table&>(rTable).mpaszResults = 0;\r
+ mnRows = rTable.mnRows;\r
+ mnCols = rTable.mnCols;\r
+ mnCurrentRow = rTable.mnCurrentRow;\r
+ return *this;\r
+}\r
+\r
+\r
+void CppSQLite3Table::finalize()\r
+{\r
+ if (mpaszResults)\r
+ {\r
+ sqlite3_free_table(mpaszResults);\r
+ mpaszResults = 0;\r
+ }\r
+}\r
+\r
+\r
+int CppSQLite3Table::numFields()\r
+{\r
+ checkResults();\r
+ return mnCols;\r
+}\r
+\r
+\r
+int CppSQLite3Table::numRows()\r
+{\r
+ checkResults();\r
+ return mnRows;\r
+}\r
+\r
+\r
+const char* CppSQLite3Table::fieldValue(int nField)\r
+{\r
+ checkResults();\r
+\r
+ if (nField < 0 || nField > mnCols-1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid field index requested",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ int nIndex = (mnCurrentRow*mnCols) + mnCols + nField;\r
+ return mpaszResults[nIndex];\r
+}\r
+\r
+\r
+const char* CppSQLite3Table::fieldValue(const char* szField)\r
+{\r
+ checkResults();\r
+\r
+ if (szField)\r
+ {\r
+ for (int nField = 0; nField < mnCols; nField++)\r
+ {\r
+ if (strcmp(szField, mpaszResults[nField]) == 0)\r
+ {\r
+ int nIndex = (mnCurrentRow*mnCols) + mnCols + nField;\r
+ return mpaszResults[nIndex];\r
+ }\r
+ }\r
+ }\r
+\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid field name requested",\r
+ DONT_DELETE_MSG);\r
+}\r
+\r
+\r
+int CppSQLite3Table::getIntField(int nField, int nNullValue/*=0*/)\r
+{\r
+ if (fieldIsNull(nField))\r
+ {\r
+ return nNullValue;\r
+ }\r
+ else\r
+ {\r
+ return atoi(fieldValue(nField));\r
+ }\r
+}\r
+\r
+\r
+int CppSQLite3Table::getIntField(const char* szField, int nNullValue/*=0*/)\r
+{\r
+ if (fieldIsNull(szField))\r
+ {\r
+ return nNullValue;\r
+ }\r
+ else\r
+ {\r
+ return atoi(fieldValue(szField));\r
+ }\r
+}\r
+\r
+\r
+double CppSQLite3Table::getFloatField(int nField, double fNullValue/*=0.0*/)\r
+{\r
+ if (fieldIsNull(nField))\r
+ {\r
+ return fNullValue;\r
+ }\r
+ else\r
+ {\r
+ return atof(fieldValue(nField));\r
+ }\r
+}\r
+\r
+\r
+double CppSQLite3Table::getFloatField(const char* szField, double fNullValue/*=0.0*/)\r
+{\r
+ if (fieldIsNull(szField))\r
+ {\r
+ return fNullValue;\r
+ }\r
+ else\r
+ {\r
+ return atof(fieldValue(szField));\r
+ }\r
+}\r
+\r
+\r
+const char* CppSQLite3Table::getStringField(int nField, const char* szNullValue/*=""*/)\r
+{\r
+ if (fieldIsNull(nField))\r
+ {\r
+ return szNullValue;\r
+ }\r
+ else\r
+ {\r
+ return fieldValue(nField);\r
+ }\r
+}\r
+\r
+\r
+const char* CppSQLite3Table::getStringField(const char* szField, const char* szNullValue/*=""*/)\r
+{\r
+ if (fieldIsNull(szField))\r
+ {\r
+ return szNullValue;\r
+ }\r
+ else\r
+ {\r
+ return fieldValue(szField);\r
+ }\r
+}\r
+\r
+\r
+bool CppSQLite3Table::fieldIsNull(int nField)\r
+{\r
+ checkResults();\r
+ return (fieldValue(nField) == 0);\r
+}\r
+\r
+\r
+bool CppSQLite3Table::fieldIsNull(const char* szField)\r
+{\r
+ checkResults();\r
+ return (fieldValue(szField) == 0);\r
+}\r
+\r
+\r
+const char* CppSQLite3Table::fieldName(int nCol)\r
+{\r
+ checkResults();\r
+\r
+ if (nCol < 0 || nCol > mnCols-1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid field index requested",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ return mpaszResults[nCol];\r
+}\r
+\r
+\r
+void CppSQLite3Table::setRow(int nRow)\r
+{\r
+ checkResults();\r
+\r
+ if (nRow < 0 || nRow > mnRows-1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid row index requested",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ mnCurrentRow = nRow;\r
+}\r
+\r
+\r
+void CppSQLite3Table::checkResults()\r
+{\r
+ if (mpaszResults == 0)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Null Results pointer",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+CppSQLite3Statement::CppSQLite3Statement()\r
+{\r
+ mpDB = 0;\r
+ mpVM = 0;\r
+}\r
+\r
+\r
+CppSQLite3Statement::CppSQLite3Statement(const CppSQLite3Statement& rStatement)\r
+{\r
+ mpDB = rStatement.mpDB;\r
+ mpVM = rStatement.mpVM;\r
+ // Only one object can own VM\r
+ const_cast<CppSQLite3Statement&>(rStatement).mpVM = 0;\r
+}\r
+\r
+\r
+CppSQLite3Statement::CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM)\r
+{\r
+ mpDB = pDB;\r
+ mpVM = pVM;\r
+}\r
+\r
+\r
+CppSQLite3Statement::~CppSQLite3Statement()\r
+{\r
+ try\r
+ {\r
+ finalize();\r
+ }\r
+ catch (...)\r
+ {\r
+ }\r
+}\r
+\r
+\r
+CppSQLite3Statement& CppSQLite3Statement::operator=(const CppSQLite3Statement& rStatement)\r
+{\r
+ mpDB = rStatement.mpDB;\r
+ mpVM = rStatement.mpVM;\r
+ // Only one object can own VM\r
+ const_cast<CppSQLite3Statement&>(rStatement).mpVM = 0;\r
+ return *this;\r
+}\r
+\r
+\r
+int CppSQLite3Statement::execDML()\r
+{\r
+ checkDB();\r
+ checkVM();\r
+\r
+ const char* szError=0;\r
+\r
+ int nRet = sqlite3_step(mpVM);\r
+\r
+ if (nRet == SQLITE_DONE)\r
+ {\r
+ int nRowsChanged = sqlite3_changes(mpDB);\r
+\r
+ nRet = sqlite3_reset(mpVM);\r
+\r
+ if (nRet != SQLITE_OK)\r
+ {\r
+ szError = sqlite3_errmsg(mpDB);\r
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);\r
+ }\r
+\r
+ return nRowsChanged;\r
+ }\r
+ else\r
+ {\r
+ nRet = sqlite3_reset(mpVM);\r
+ szError = sqlite3_errmsg(mpDB);\r
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+CppSQLite3Query CppSQLite3Statement::execQuery()\r
+{\r
+ checkDB();\r
+ checkVM();\r
+\r
+ int nRet = sqlite3_step(mpVM);\r
+\r
+ if (nRet == SQLITE_DONE)\r
+ {\r
+ // no rows\r
+ return CppSQLite3Query(mpDB, mpVM, true/*eof*/, false);\r
+ }\r
+ else if (nRet == SQLITE_ROW)\r
+ {\r
+ // at least 1 row\r
+ return CppSQLite3Query(mpDB, mpVM, false/*eof*/, false);\r
+ }\r
+ else\r
+ {\r
+ nRet = sqlite3_reset(mpVM);\r
+ const char* szError = sqlite3_errmsg(mpDB);\r
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Statement::bind(int nParam, const char* szValue)\r
+{\r
+ checkVM();\r
+ int nRes = sqlite3_bind_text(mpVM, nParam, szValue, -1, SQLITE_TRANSIENT);\r
+\r
+ if (nRes != SQLITE_OK)\r
+ {\r
+ throw CppSQLite3Exception(nRes,\r
+ "Error binding string param",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Statement::bind(int nParam, const int nValue)\r
+{\r
+ checkVM();\r
+ int nRes = sqlite3_bind_int(mpVM, nParam, nValue);\r
+\r
+ if (nRes != SQLITE_OK)\r
+ {\r
+ throw CppSQLite3Exception(nRes,\r
+ "Error binding int param",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Statement::bind(int nParam, const double dValue)\r
+{\r
+ checkVM();\r
+ int nRes = sqlite3_bind_double(mpVM, nParam, dValue);\r
+\r
+ if (nRes != SQLITE_OK)\r
+ {\r
+ throw CppSQLite3Exception(nRes,\r
+ "Error binding double param",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Statement::bind(int nParam, const unsigned char* blobValue, int nLen)\r
+{\r
+ checkVM();\r
+ int nRes = sqlite3_bind_blob(mpVM, nParam,\r
+ (const void*)blobValue, nLen, SQLITE_TRANSIENT);\r
+\r
+ if (nRes != SQLITE_OK)\r
+ {\r
+ throw CppSQLite3Exception(nRes,\r
+ "Error binding blob param",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+ \r
+void CppSQLite3Statement::bindNull(int nParam)\r
+{\r
+ checkVM();\r
+ int nRes = sqlite3_bind_null(mpVM, nParam);\r
+\r
+ if (nRes != SQLITE_OK)\r
+ {\r
+ throw CppSQLite3Exception(nRes,\r
+ "Error binding NULL param",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Statement::reset()\r
+{\r
+ if (mpVM)\r
+ {\r
+ int nRet = sqlite3_reset(mpVM);\r
+\r
+ if (nRet != SQLITE_OK)\r
+ {\r
+ const char* szError = sqlite3_errmsg(mpDB);\r
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);\r
+ }\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Statement::finalize()\r
+{\r
+ if (mpVM)\r
+ {\r
+ int nRet = sqlite3_finalize(mpVM);\r
+ mpVM = 0;\r
+\r
+ if (nRet != SQLITE_OK)\r
+ {\r
+ const char* szError = sqlite3_errmsg(mpDB);\r
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);\r
+ }\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Statement::checkDB()\r
+{\r
+ if (mpDB == 0)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Database not open",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+void CppSQLite3Statement::checkVM()\r
+{\r
+ if (mpVM == 0)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Null Virtual Machine pointer",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+CppSQLite3DB::CppSQLite3DB()\r
+{\r
+ mpDB = 0;\r
+ mnBusyTimeoutMs = 60000; // 60 seconds\r
+}\r
+\r
+\r
+CppSQLite3DB::CppSQLite3DB(const CppSQLite3DB& db)\r
+{\r
+ mpDB = db.mpDB;\r
+ mnBusyTimeoutMs = 60000; // 60 seconds\r
+}\r
+\r
+\r
+CppSQLite3DB::~CppSQLite3DB()\r
+{\r
+ close();\r
+}\r
+\r
+\r
+CppSQLite3DB& CppSQLite3DB::operator=(const CppSQLite3DB& db)\r
+{\r
+ mpDB = db.mpDB;\r
+ mnBusyTimeoutMs = 60000; // 60 seconds\r
+ return *this;\r
+}\r
+\r
+\r
+void CppSQLite3DB::open(const char* szFile)\r
+{\r
+ int nRet = sqlite3_open(szFile, &mpDB);\r
+\r
+ if (nRet != SQLITE_OK)\r
+ {\r
+ const char* szError = sqlite3_errmsg(mpDB);\r
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);\r
+ }\r
+\r
+ setBusyTimeout(mnBusyTimeoutMs);\r
+}\r
+\r
+\r
+void CppSQLite3DB::close()\r
+{\r
+ if (mpDB)\r
+ {\r
+ sqlite3_close(mpDB);\r
+ mpDB = 0;\r
+ }\r
+}\r
+\r
+\r
+CppSQLite3Statement CppSQLite3DB::compileStatement(const char* szSQL)\r
+{\r
+ checkDB();\r
+\r
+ sqlite3_stmt* pVM = compile(szSQL);\r
+ return CppSQLite3Statement(mpDB, pVM);\r
+}\r
+\r
+\r
+bool CppSQLite3DB::tableExists(const char* szTable)\r
+{\r
+ char szSQL[128];\r
+ sprintf(szSQL,\r
+ "select count(*) from sqlite_master where type='table' and name='%s'",\r
+ szTable);\r
+ int nRet = execScalar(szSQL);\r
+ return (nRet > 0);\r
+}\r
+\r
+\r
+int CppSQLite3DB::execDML(const char* szSQL)\r
+{\r
+ checkDB();\r
+\r
+ char* szError=0;\r
+\r
+ int nRet = sqlite3_exec(mpDB, szSQL, 0, 0, &szError);\r
+\r
+ if (nRet == SQLITE_OK)\r
+ {\r
+ return sqlite3_changes(mpDB);\r
+ }\r
+ else\r
+ {\r
+ throw CppSQLite3Exception(nRet, szError);\r
+ }\r
+}\r
+\r
+\r
+CppSQLite3Query CppSQLite3DB::execQuery(const char* szSQL)\r
+{\r
+ checkDB();\r
+\r
+ sqlite3_stmt* pVM = compile(szSQL);\r
+\r
+ int nRet = sqlite3_step(pVM);\r
+\r
+ if (nRet == SQLITE_DONE)\r
+ {\r
+ // no rows\r
+ return CppSQLite3Query(mpDB, pVM, true/*eof*/);\r
+ }\r
+ else if (nRet == SQLITE_ROW)\r
+ {\r
+ // at least 1 row\r
+ return CppSQLite3Query(mpDB, pVM, false/*eof*/);\r
+ }\r
+ else\r
+ {\r
+ nRet = sqlite3_finalize(pVM);\r
+ const char* szError= sqlite3_errmsg(mpDB);\r
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+int CppSQLite3DB::execScalar(const char* szSQL)\r
+{\r
+ CppSQLite3Query q = execQuery(szSQL);\r
+\r
+ if (q.eof() || q.numFields() < 1)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Invalid scalar query",\r
+ DONT_DELETE_MSG);\r
+ }\r
+\r
+ return atoi(q.fieldValue(0));\r
+}\r
+\r
+\r
+CppSQLite3Table CppSQLite3DB::getTable(const char* szSQL)\r
+{\r
+ checkDB();\r
+\r
+ char* szError=0;\r
+ char** paszResults=0;\r
+ int nRet;\r
+ int nRows(0);\r
+ int nCols(0);\r
+\r
+ nRet = sqlite3_get_table(mpDB, szSQL, &paszResults, &nRows, &nCols, &szError);\r
+\r
+ if (nRet == SQLITE_OK)\r
+ {\r
+ return CppSQLite3Table(paszResults, nRows, nCols);\r
+ }\r
+ else\r
+ {\r
+ throw CppSQLite3Exception(nRet, szError);\r
+ }\r
+}\r
+\r
+\r
+sqlite_int64 CppSQLite3DB::lastRowId()\r
+{\r
+ return sqlite3_last_insert_rowid(mpDB);\r
+}\r
+\r
+\r
+void CppSQLite3DB::setBusyTimeout(int nMillisecs)\r
+{\r
+ mnBusyTimeoutMs = nMillisecs;\r
+ sqlite3_busy_timeout(mpDB, mnBusyTimeoutMs);\r
+}\r
+\r
+\r
+void CppSQLite3DB::checkDB()\r
+{\r
+ if (!mpDB)\r
+ {\r
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,\r
+ "Database not open",\r
+ DONT_DELETE_MSG);\r
+ }\r
+}\r
+\r
+\r
+sqlite3_stmt* CppSQLite3DB::compile(const char* szSQL)\r
+{\r
+ checkDB();\r
+\r
+ char* szError=0;\r
+ const char* szTail=0;\r
+ sqlite3_stmt* pVM;\r
+\r
+ int nRet = sqlite3_prepare(mpDB, szSQL, -1, &pVM, &szTail);\r
+\r
+ if (nRet != SQLITE_OK)\r
+ {\r
+ throw CppSQLite3Exception(nRet, szError);\r
+ }\r
+\r
+ return pVM;\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// SQLite encode.c reproduced here, containing implementation notes and source\r
+// for sqlite3_encode_binary() and sqlite3_decode_binary() \r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+/*\r
+** 2002 April 25\r
+**\r
+** The author disclaims copyright to this source code. In place of\r
+** a legal notice, here is a blessing:\r
+**\r
+** May you do good and not evil.\r
+** May you find forgiveness for yourself and forgive others.\r
+** May you share freely, never taking more than you give.\r
+**\r
+*************************************************************************\r
+** This file contains helper routines used to translate binary data into\r
+** a null-terminated string (suitable for use in SQLite) and back again.\r
+** These are convenience routines for use by people who want to store binary\r
+** data in an SQLite database. The code in this file is not used by any other\r
+** part of the SQLite library.\r
+**\r
+** $Id: CppSQLite3.cpp,v 1.1 2009/02/09 10:09:33 guigues Exp $\r
+*/\r
+\r
+/*\r
+** How This Encoder Works\r
+**\r
+** The output is allowed to contain any character except 0x27 (') and\r
+** 0x00. This is accomplished by using an escape character to encode\r
+** 0x27 and 0x00 as a two-byte sequence. The escape character is always\r
+** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The\r
+** 0x27 character is encoded as the two byte sequence 0x01 0x03. Finally,\r
+** the escape character itself is encoded as the two-character sequence\r
+** 0x01 0x02.\r
+**\r
+** To summarize, the encoder works by using an escape sequences as follows:\r
+**\r
+** 0x00 -> 0x01 0x01\r
+** 0x01 -> 0x01 0x02\r
+** 0x27 -> 0x01 0x03\r
+**\r
+** If that were all the encoder did, it would work, but in certain cases\r
+** it could double the size of the encoded string. For example, to\r
+** encode a string of 100 0x27 characters would require 100 instances of\r
+** the 0x01 0x03 escape sequence resulting in a 200-character output.\r
+** We would prefer to keep the size of the encoded string smaller than\r
+** this.\r
+**\r
+** To minimize the encoding size, we first add a fixed offset value to each \r
+** byte in the sequence. The addition is modulo 256. (That is to say, if\r
+** the sum of the original character value and the offset exceeds 256, then\r
+** the higher order bits are truncated.) The offset is chosen to minimize\r
+** the number of characters in the string that need to be escaped. For\r
+** example, in the case above where the string was composed of 100 0x27\r
+** characters, the offset might be 0x01. Each of the 0x27 characters would\r
+** then be converted into an 0x28 character which would not need to be\r
+** escaped at all and so the 100 character input string would be converted\r
+** into just 100 characters of output. Actually 101 characters of output - \r
+** we have to record the offset used as the first byte in the sequence so\r
+** that the string can be decoded. Since the offset value is stored as\r
+** part of the output string and the output string is not allowed to contain\r
+** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27.\r
+**\r
+** Here, then, are the encoding steps:\r
+**\r
+** (1) Choose an offset value and make it the first character of\r
+** output.\r
+**\r
+** (2) Copy each input character into the output buffer, one by\r
+** one, adding the offset value as you copy.\r
+**\r
+** (3) If the value of an input character plus offset is 0x00, replace\r
+** that one character by the two-character sequence 0x01 0x01.\r
+** If the sum is 0x01, replace it with 0x01 0x02. If the sum\r
+** is 0x27, replace it with 0x01 0x03.\r
+**\r
+** (4) Put a 0x00 terminator at the end of the output.\r
+**\r
+** Decoding is obvious:\r
+**\r
+** (5) Copy encoded characters except the first into the decode \r
+** buffer. Set the first encoded character aside for use as\r
+** the offset in step 7 below.\r
+**\r
+** (6) Convert each 0x01 0x01 sequence into a single character 0x00.\r
+** Convert 0x01 0x02 into 0x01. Convert 0x01 0x03 into 0x27.\r
+**\r
+** (7) Subtract the offset value that was the first character of\r
+** the encoded buffer from all characters in the output buffer.\r
+**\r
+** The only tricky part is step (1) - how to compute an offset value to\r
+** minimize the size of the output buffer. This is accomplished by testing\r
+** all offset values and picking the one that results in the fewest number\r
+** of escapes. To do that, we first scan the entire input and count the\r
+** number of occurances of each character value in the input. Suppose\r
+** the number of 0x00 characters is N(0), the number of occurances of 0x01\r
+** is N(1), and so forth up to the number of occurances of 0xff is N(255).\r
+** An offset of 0 is not allowed so we don't have to test it. The number\r
+** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number\r
+** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth.\r
+** In this way we find the offset that gives the minimum number of escapes,\r
+** and thus minimizes the length of the output string.\r
+*/\r
+\r
+/*\r
+** Encode a binary buffer "in" of size n bytes so that it contains\r
+** no instances of characters '\'' or '\000'. The output is \r
+** null-terminated and can be used as a string value in an INSERT\r
+** or UPDATE statement. Use sqlite3_decode_binary() to convert the\r
+** string back into its original binary.\r
+**\r
+** The result is written into a preallocated output buffer "out".\r
+** "out" must be able to hold at least 2 +(257*n)/254 bytes.\r
+** In other words, the output will be expanded by as much as 3\r
+** bytes for every 254 bytes of input plus 2 bytes of fixed overhead.\r
+** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.)\r
+**\r
+** The return value is the number of characters in the encoded\r
+** string, excluding the "\000" terminator.\r
+*/\r
+int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out){\r
+ int i, j, e, m;\r
+ int cnt[256];\r
+ if( n<=0 ){\r
+ out[0] = 'x';\r
+ out[1] = 0;\r
+ return 1;\r
+ }\r
+ memset(cnt, 0, sizeof(cnt));\r
+ for(i=n-1; i>=0; i--){ cnt[in[i]]++; }\r
+ m = n;\r
+ for(i=1; i<256; i++){\r
+ int sum;\r
+ if( i=='\'' ) continue;\r
+ sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff];\r
+ if( sum<m ){\r
+ m = sum;\r
+ e = i;\r
+ if( m==0 ) break;\r
+ }\r
+ }\r
+ out[0] = e;\r
+ j = 1;\r
+ for(i=0; i<n; i++){\r
+ int c = (in[i] - e)&0xff;\r
+ if( c==0 ){\r
+ out[j++] = 1;\r
+ out[j++] = 1;\r
+ }else if( c==1 ){\r
+ out[j++] = 1;\r
+ out[j++] = 2;\r
+ }else if( c=='\'' ){\r
+ out[j++] = 1;\r
+ out[j++] = 3;\r
+ }else{\r
+ out[j++] = c;\r
+ }\r
+ }\r
+ out[j] = 0;\r
+ return j;\r
+}\r
+\r
+/*\r
+** Decode the string "in" into binary data and write it into "out".\r
+** This routine reverses the encoding created by sqlite3_encode_binary().\r
+** The output will always be a few bytes less than the input. The number\r
+** of bytes of output is returned. If the input is not a well-formed\r
+** encoding, -1 is returned.\r
+**\r
+** The "in" and "out" parameters may point to the same buffer in order\r
+** to decode a string in place.\r
+*/\r
+int sqlite3_decode_binary(const unsigned char *in, unsigned char *out){\r
+ int i, c, e;\r
+ e = *(in++);\r
+ i = 0;\r
+ while( (c = *(in++))!=0 ){\r
+ if( c==1 ){\r
+ c = *(in++);\r
+ if( c==1 ){\r
+ c = 0;\r
+ }else if( c==2 ){\r
+ c = 1;\r
+ }else if( c==3 ){\r
+ c = '\'';\r
+ }else{\r
+ return -1;\r
+ }\r
+ }\r
+ out[i++] = (c + e)&0xff;\r
+ }\r
+ return i;\r
+}\r
--- /dev/null
+////////////////////////////////////////////////////////////////////////////////\r
+// CppSQLite3 - A C++ wrapper around the SQLite3 embedded database library.\r
+//\r
+// Copyright (c) 2004 Rob Groves. All Rights Reserved. rob.groves@btinternet.com\r
+// \r
+// Permission to use, copy, modify, and distribute this software and its\r
+// documentation for any purpose, without fee, and without a written\r
+// agreement, is hereby granted, provided that the above copyright notice, \r
+// this paragraph and the following two paragraphs appear in all copies, \r
+// modifications, and distributions.\r
+//\r
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,\r
+// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST\r
+// PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,\r
+// EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+//\r
+// THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT\r
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\r
+// PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF\r
+// ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". THE AUTHOR HAS NO OBLIGATION\r
+// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\r
+//\r
+// V3.0 03/08/2004 -Initial Version for sqlite3\r
+//\r
+// V3.1 16/09/2004 -Implemented getXXXXField using sqlite3 functions\r
+// -Added CppSQLiteDB3::tableExists()\r
+////////////////////////////////////////////////////////////////////////////////\r
+#ifndef _CppSQLite3_H_\r
+#define _CppSQLite3_H_\r
+\r
+#include "sqlite3.h"\r
+#include <cstdio>\r
+#include <cstring>\r
+\r
+#define CPPSQLITE_ERROR 1000\r
+\r
+class CppSQLite3Exception\r
+{\r
+public:\r
+\r
+ CppSQLite3Exception(const int nErrCode,\r
+ char* szErrMess,\r
+ bool bDeleteMsg=true);\r
+ CppSQLite3Exception(const int nErrCode,\r
+ const char* szErrMess,\r
+ bool bDeleteMsg=true);\r
+ \r
+ CppSQLite3Exception(const CppSQLite3Exception& e);\r
+\r
+ virtual ~CppSQLite3Exception();\r
+\r
+ const int errorCode() { return mnErrCode; }\r
+\r
+ const char* errorMessage() { return mpszErrMess; }\r
+\r
+ static const char* errorCodeAsString(int nErrCode);\r
+\r
+private:\r
+\r
+ int mnErrCode;\r
+ char* mpszErrMess;\r
+};\r
+\r
+\r
+class CppSQLite3Buffer\r
+{\r
+public:\r
+\r
+ CppSQLite3Buffer();\r
+\r
+ ~CppSQLite3Buffer();\r
+\r
+ const char* format(const char* szFormat, ...);\r
+\r
+ operator const char*() { return mpBuf; }\r
+\r
+ void clear();\r
+\r
+private:\r
+\r
+ char* mpBuf;\r
+};\r
+\r
+\r
+class CppSQLite3Binary\r
+{\r
+public:\r
+\r
+ CppSQLite3Binary();\r
+\r
+ ~CppSQLite3Binary();\r
+\r
+ void setBinary(const unsigned char* pBuf, int nLen);\r
+ void setEncoded(const unsigned char* pBuf);\r
+\r
+ const unsigned char* getEncoded();\r
+ const unsigned char* getBinary();\r
+\r
+ int getBinaryLength();\r
+\r
+ unsigned char* allocBuffer(int nLen);\r
+\r
+ void clear();\r
+\r
+private:\r
+\r
+ unsigned char* mpBuf;\r
+ int mnBinaryLen;\r
+ int mnBufferLen;\r
+ int mnEncodedLen;\r
+ bool mbEncoded;\r
+};\r
+\r
+\r
+class CppSQLite3Query\r
+{\r
+public:\r
+\r
+ CppSQLite3Query();\r
+\r
+ CppSQLite3Query(const CppSQLite3Query& rQuery);\r
+\r
+ CppSQLite3Query(sqlite3* pDB,\r
+ sqlite3_stmt* pVM,\r
+ bool bEof,\r
+ bool bOwnVM=true);\r
+\r
+ CppSQLite3Query& operator=(const CppSQLite3Query& rQuery);\r
+\r
+ virtual ~CppSQLite3Query();\r
+\r
+ int numFields();\r
+\r
+ int fieldIndex(const char* szField);\r
+ const char* fieldName(int nCol);\r
+\r
+ const char* fieldDeclType(int nCol);\r
+ int fieldDataType(int nCol);\r
+\r
+ const char* fieldValue(int nField);\r
+ const char* fieldValue(const char* szField);\r
+\r
+ int getIntField(int nField, int nNullValue=0);\r
+ int getIntField(const char* szField, int nNullValue=0);\r
+\r
+ double getFloatField(int nField, double fNullValue=0.0);\r
+ double getFloatField(const char* szField, double fNullValue=0.0);\r
+\r
+ const char* getStringField(int nField, const char* szNullValue="");\r
+ const char* getStringField(const char* szField, const char* szNullValue="");\r
+\r
+ const unsigned char* getBlobField(int nField, int& nLen);\r
+ const unsigned char* getBlobField(const char* szField, int& nLen);\r
+\r
+ bool fieldIsNull(int nField);\r
+ bool fieldIsNull(const char* szField);\r
+\r
+ bool eof();\r
+\r
+ void nextRow();\r
+\r
+ void finalize();\r
+\r
+private:\r
+\r
+ void checkVM();\r
+\r
+ sqlite3* mpDB;\r
+ sqlite3_stmt* mpVM;\r
+ bool mbEof;\r
+ int mnCols;\r
+ bool mbOwnVM;\r
+};\r
+\r
+\r
+class CppSQLite3Table\r
+{\r
+public:\r
+\r
+ CppSQLite3Table();\r
+\r
+ CppSQLite3Table(const CppSQLite3Table& rTable);\r
+\r
+ CppSQLite3Table(char** paszResults, int nRows, int nCols);\r
+\r
+ virtual ~CppSQLite3Table();\r
+\r
+ CppSQLite3Table& operator=(const CppSQLite3Table& rTable);\r
+\r
+ int numFields();\r
+\r
+ int numRows();\r
+\r
+ const char* fieldName(int nCol);\r
+\r
+ const char* fieldValue(int nField);\r
+ const char* fieldValue(const char* szField);\r
+\r
+ int getIntField(int nField, int nNullValue=0);\r
+ int getIntField(const char* szField, int nNullValue=0);\r
+\r
+ double getFloatField(int nField, double fNullValue=0.0);\r
+ double getFloatField(const char* szField, double fNullValue=0.0);\r
+\r
+ const char* getStringField(int nField, const char* szNullValue="");\r
+ const char* getStringField(const char* szField, const char* szNullValue="");\r
+\r
+ bool fieldIsNull(int nField);\r
+ bool fieldIsNull(const char* szField);\r
+\r
+ void setRow(int nRow);\r
+\r
+ void finalize();\r
+\r
+private:\r
+\r
+ void checkResults();\r
+\r
+ int mnCols;\r
+ int mnRows;\r
+ int mnCurrentRow;\r
+ char** mpaszResults;\r
+};\r
+\r
+\r
+class CppSQLite3Statement\r
+{\r
+public:\r
+\r
+ CppSQLite3Statement();\r
+\r
+ CppSQLite3Statement(const CppSQLite3Statement& rStatement);\r
+\r
+ CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM);\r
+\r
+ virtual ~CppSQLite3Statement();\r
+\r
+ CppSQLite3Statement& operator=(const CppSQLite3Statement& rStatement);\r
+\r
+ int execDML();\r
+\r
+ CppSQLite3Query execQuery();\r
+\r
+ void bind(int nParam, const char* szValue);\r
+ void bind(int nParam, const int nValue);\r
+ void bind(int nParam, const double dwValue);\r
+ void bind(int nParam, const unsigned char* blobValue, int nLen);\r
+ void bindNull(int nParam);\r
+\r
+ void reset();\r
+\r
+ void finalize();\r
+\r
+private:\r
+\r
+ void checkDB();\r
+ void checkVM();\r
+\r
+ sqlite3* mpDB;\r
+ sqlite3_stmt* mpVM;\r
+};\r
+\r
+\r
+class CppSQLite3DB\r
+{\r
+public:\r
+\r
+ CppSQLite3DB();\r
+\r
+ virtual ~CppSQLite3DB();\r
+\r
+ void open(const char* szFile);\r
+\r
+ void close();\r
+\r
+ bool tableExists(const char* szTable);\r
+\r
+ int execDML(const char* szSQL);\r
+\r
+ CppSQLite3Query execQuery(const char* szSQL);\r
+\r
+ int execScalar(const char* szSQL);\r
+\r
+ CppSQLite3Table getTable(const char* szSQL);\r
+\r
+ CppSQLite3Statement compileStatement(const char* szSQL);\r
+\r
+ sqlite_int64 lastRowId();\r
+\r
+ void interrupt() { sqlite3_interrupt(mpDB); }\r
+\r
+ void setBusyTimeout(int nMillisecs);\r
+\r
+ static const char* SQLiteVersion() { return SQLITE_VERSION; }\r
+\r
+private:\r
+\r
+ CppSQLite3DB(const CppSQLite3DB& db);\r
+ CppSQLite3DB& operator=(const CppSQLite3DB& db);\r
+\r
+ sqlite3_stmt* compile(const char* szSQL);\r
+\r
+ void checkDB();\r
+\r
+ sqlite3* mpDB;\r
+ int mnBusyTimeoutMs;\r
+};\r
+\r
+#endif\r
--- /dev/null
+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
+...
--- /dev/null
+#include <creaImageIOGimmick.h>
+
+#include <creaMessageManager.h>
+
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+
+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 '"<<GetLocalDatabasePath()<<"' "
+ << "does not exist : creating it"<<std::endl);
+
+ // CREATING DEFAULT DB STRUCTURE
+ mLocalDatabase->GetTree().GetDescriptor().CreateDefault();
+
+ if ( ! mLocalDatabase->Create(true) )
+ {
+ creaMessage("Gimmick!",1,
+ "[Gimmick!] !! ERROR CREATING '"<<GetLocalDatabasePath()<<"'");
+ return false;
+ }
+ }
+ else
+ {
+ /// Open and test it
+ creaMessage("Gimmick!",1,
+ "[Gimmick!] Opening local database '"
+ <<GetLocalDatabasePath()<<"' "
+ <<std::endl);
+ if ( ! mLocalDatabase->Open(true) )
+ {
+ creaMessage("Gimmick!",1,
+ "[Gimmick!] !! ERROR OPENING '"<<GetLocalDatabasePath()<<"'");
+ return false;
+ }
+
+ }
+ return true;
+
+ }
+ //================================================================
+
+
+ //==============================================================
+ bool Gimmick::Finalize()
+ {
+ delete mLocalDatabase;
+ }
+ //==============================================================
+
+ //================================================================
+ // file separator
+#if defined(_WIN32)
+#define VALID_FILE_SEPARATOR "\\"
+#define INVALID_FILE_SEPARATOR "/"
+#else
+#define INVALID_FILE_SEPARATOR "\\"
+#define VALID_FILE_SEPARATOR "/"
+#endif
+ //================================================================
+
+ //================================================================
+ const std::string& Gimmick::GetHomeDirectory()
+ {
+ if (mHomeDirectory.size()==0)
+ {
+#if defined(__GNUC__)
+ mHomeDirectory = getenv("HOME");
+#elif defined(_WIN32)
+ mHomeDirectory = getenv("USERPROFILE");
+#endif
+ }
+ return mHomeDirectory;
+ }
+ //================================================================
+ const std::string& Gimmick::GetUserSettingsDirectory()
+ {
+ if (mUserSettingsDirectory.size()==0)
+ {
+ mUserSettingsDirectory = GetHomeDirectory();
+ mUserSettingsDirectory += "/.gimmick/";
+ boost::algorithm::replace_all( mUserSettingsDirectory,
+ INVALID_FILE_SEPARATOR ,
+ VALID_FILE_SEPARATOR);
+ }
+ return mUserSettingsDirectory;
+ }
+ //================================================================
+
+ //================================================================
+ const std::string& Gimmick::GetLocalDatabasePath()
+ {
+ if (mLocalDatabasePath.size()==0)
+ {
+ mLocalDatabasePath = GetUserSettingsDirectory();
+ mLocalDatabasePath += "local_database.sqlite3";
+ boost::algorithm::replace_all( mLocalDatabasePath,
+ INVALID_FILE_SEPARATOR ,
+ VALID_FILE_SEPARATOR);
+ }
+ return mLocalDatabasePath;
+ }
+ //========================================================================
+
+ //========================================================================
+ bool Gimmick::CreateUserSettingsDirectory()
+ {
+ if (! boost::filesystem::is_directory( GetUserSettingsDirectory() ) )
+ {
+ creaMessage("Gimmick!",1,
+ "[Gimmick!] Directory '"<<GetUserSettingsDirectory()<<"' "
+ << "does not exist : creating it"<<std::endl);
+
+ if ( ! boost::filesystem::create_directory( GetUserSettingsDirectory() ) )
+ {
+ creaMessage("Gimmick!",1,
+ "[Gimmick!] !! ERROR CREATING '"<<GetUserSettingsDirectory()<<"'");
+ return false;
+ }
+ }
+ return true;
+ }
+ //========================================================================
+
+}
--- /dev/null
+#ifndef __creaImageIOGimmick_h_INCLUDED__
+#define __creaImageIOGimmick_h_INCLUDED__
+
+#include <creaImageIOSQLiteTreeHandler.h>
+
+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
+
+
--- /dev/null
+#include <creaImageIOImageFinder.h>
+#include <creaWx.h>
+#include <wx/dir.h>
+#include <wx/filename.h>
+
+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<std::string>& 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<std::string>::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<std::string> &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"<<std::endl;
+ sw.Start(0);
+ if (!progress->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"
+ // << " '"<<directory<<"'"<<std::endl;
+ // std::cout << "------ Parsing directory ------"<<std::endl;
+
+ if (progress)
+ {
+ progress->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<std::string> 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<std::string>::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;
+ }
+ //=====================================================================
+
+
+
+}
--- /dev/null
+#ifndef __creaImageIOImageFinder_h_INCLUDED__
+#define __creaImageIOImageFinder_h_INCLUDED__
+
+#include <creaImageIOTreeHandler.h>
+#include <creaImageIOImageReader.h>
+#include <wx/wx.h>
+#include <wx/progdlg.h>
+
+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<std::string>& 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<std::string> &Filenames,
+ bool recurse,
+ wxProgressDialog* progress,
+ UpdateSummary& summary);
+ private:
+ TreeHandler* mTreeHandler;
+ ImageReader mReader;
+ wxStopWatch msw[10];
+
+ };
+ // EO class ImageFinder
+ //=======================================================================
+
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOImageReader.h>
+#include <creaImageIOTreeAttributeDescriptor.h>
+
+
+#include <vtkImageReader2.h>
+#include <vtkPNGReader.h>
+#include <vtkTIFFReader.h>
+#include <vtkJPEGReader.h>
+#include <vtkBMPReader.h>
+#include <vtkSLCReader.h>
+#include <vtkMetaImageReader.h>
+//#include <vtkGESignalReader.h>
+
+#include <gdcmFile.h>
+#include <vtkGdcmReader.h>
+
+
+#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<std::string>& 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<std::string>&) {}
+
+ 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<std::string,std::string>& 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 "<<GetName()
+ //<<" ::CanRead("<<filename<<")"
+ // <<std::endl;
+ return (mVTKReader->CanReadFile(filename.c_str())!=0);
+ }
+ //=====================================================================
+
+ //=====================================================================
+ vtkImageData* ReadImage(const std::string& filename)
+ {
+ // std::cout << "## Reader "<<GetName()
+ //<<" ::Read("<<filename<<")"
+ // <<std::endl;
+ vtkImageData* im = 0;
+ try
+ {
+ mVTKReader->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<std::string>& v)
+ {
+ std::string ext = mExtensions;
+ if (ext.size()==0) ext = mVTKReader->GetFileExtensions ();
+
+ IRSplitString(ext," ",v);
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void ReadAttributes(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+ // std::cout << "SpecificVtkReader::ReadDicomInfo '"<<filename<<"'"<<std::endl;
+ // Get image dimensions
+ // How to get the image info without loading it in vtk ?
+ mVTKReader->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<std::string,std::string>::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 "<<GetName()
+ //<<" ::CanRead("<<filename<<")"
+ // <<std::endl;
+ // return true;
+
+
+ // GDCM_NAME_SPACE
+ // std::cout << "GDCM_NAME_SPACE = '" << STRINGIFY_SYMBOL(GDCM_NAME_SPACE)
+ // << "'"
+ // <<std::endl;
+
+ GDCM_NAME_SPACE::File* file = GDCM_NAME_SPACE::File::New();
+ file->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 "<<GetName()
+ //<<" ::Read("<<filename<<")"
+ // <<std::endl;
+
+ vtkImageData* im = 0;
+ try
+ {
+ mReader->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<std::string>& v)
+ {
+ v.push_back("dcm");
+ v.push_back("");
+ }
+ //=====================================================================
+
+ //=====================================================================
+ //=====================================================================
+ void ReadAttributes(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+ // std::cout << "DicomReader::ReadDicomInfo '"<<filename<<"'"<<std::endl;
+ GDCM_NAME_SPACE::File* file = GDCM_NAME_SPACE::File::New();
+ file->SetLoadMode( GDCM_NAME_SPACE::LD_ALL);
+ file->SetFileName(filename.c_str());
+ file->Load();
+ if (file->IsReadable())
+ {
+
+ std::map<std::string,std::string>::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()"<<std::endl;
+ if (mUnreadableImage!=0) return;
+
+ Register(new SpecificVtkReader(vtkPNGReader::New()));
+ Register(new SpecificVtkReader(vtkTIFFReader::New()));
+ Register(new SpecificVtkReader(vtkJPEGReader::New()));
+ Register(new SpecificVtkReader(vtkBMPReader::New()));
+ Register(new SpecificVtkReader(vtkSLCReader::New()));
+ Register(new SpecificVtkReader(vtkMetaImageReader::New(),"MHD",".mhd"));
+ // Register(new SpecificVtkReader(vtkGESignalReader::New()));
+ Register(new DicomReader);
+
+ /*
+ std::cout << "## Registered file extensions : "<<std::endl;
+ std::vector<std::string>::const_iterator i;
+ for (i=GetKnownExtensions().begin();
+ i!=GetKnownExtensions().end();
+ i++)
+ {
+ std::cout << "'"<<(*i)<<"'"<<std::endl;
+ }
+ */
+ //
+ mUnreadableImage = vtkImageData::New();
+ int dim[3];
+ dim[0] = dim[1] = 128;
+ dim[2] = 1;
+ mUnreadableImage->SetDimensions ( dim );
+ mUnreadableImage->SetScalarTypeToUnsignedChar();
+ mUnreadableImage->AllocateScalars();
+ for (int i=0;i<dim[0];i++)
+ for (int j=0;j<dim[1];j++)
+ mUnreadableImage->SetScalarComponentFromFloat(i,j,0,0,0);
+ for (int i=0;i<dim[0];i++)
+ {
+ mUnreadableImage->SetScalarComponentFromFloat(i,i,0,0,255);
+ mUnreadableImage->SetScalarComponentFromFloat(dim[0]-1-i,i,0,0,255);
+ }
+
+
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ ImageReader::~ImageReader()
+ {
+ // std::cout << "#### ImageReader::~ImageReader()"<<std::endl;
+ std::vector<SpecificImageReader*>::iterator i;
+ for (i=mReader.begin(); i!=mReader.end(); i++)
+ {
+ // std::cout << "#### ImageReader::UnRegister("
+ // << (*i)->GetName()<<")"<<std::endl;
+ delete (*i);
+ }
+ mReader.clear();
+ if (mUnreadableImage!=0)
+ {
+ mUnreadableImage->Delete();
+ mUnreadableImage = 0;
+ }
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void ImageReader::Register(SpecificImageReader* r)
+ {
+ // std::cout << "#### ImageReader::Register("<<r->GetName()<<")"<<std::endl;
+ mReader.push_back(r);
+ r->PushBackExtensions(mKnownExtensions);
+ }
+ //=====================================================================
+
+ //=====================================================================
+ // Returns true iff the file is readable
+ bool ImageReader::CanRead( const std::string& filename,
+ const std::string& exclude )
+ {
+ // std::cout << "## ImageReader::CanRead("<<filename<<")"<<std::endl;
+ bool ok = false;
+ std::vector<SpecificImageReader*>::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("<<filename<<")"<<std::endl;
+ if (mLastFilename!=filename)
+ {
+ if (!CanRead(filename,exclude))
+ {
+ // std::cout << " -- Cannot read image "<<std::endl;
+ vtkImageData* im = vtkImageData::New();
+ im->ShallowCopy(mUnreadableImage);
+ return im;
+ }
+ }
+ vtkImageData* i = mLastReader->ReadImage(mLastFilename);
+ // std::cout << "i="<<i<<std::endl;
+ if (i==0)
+ {
+ // std::cout << "i=UNREAD"<<i<<std::endl;
+ i = vtkImageData::New();
+ i->ShallowCopy(mUnreadableImage);
+ }
+ // std::cout << "i="<<i<<std::endl;
+ return i;
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ void ImageReader::ReadAttributes(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+ // std::cout << "ImageReader::ReadDicomInfo '"<<filename<<"'"<<std::endl;
+ // std::cout << "## ImageReader::Read("<<filename<<")"<<std::endl;
+ if (mLastFilename!=filename)
+ {
+ if (!CanRead(filename))
+ {
+ return;
+ }
+ }
+ mLastReader->ReadAttributes(mLastFilename,attr);
+ }
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
--- /dev/null
+#ifndef __creaImageIOImageReader_h_INCLUDED__
+#define __creaImageIOImageReader_h_INCLUDED__
+
+
+#include <creaImageIOTreeAttributeDescriptor.h>
+
+#include <vtkImageData.h>
+#include <string>
+#include <vector>
+#include <map>
+
+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<std::string>& 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<std::string,std::string>& attr);
+
+ protected:
+
+ void Register(SpecificImageReader*);
+
+ std::vector<SpecificImageReader*> mReader;
+ std::vector<std::string> mKnownExtensions;
+ vtkImageData* mUnreadableImage;
+
+ std::string mLastFilename;
+ SpecificImageReader* mLastReader;
+
+ private:
+
+ }; // class ImageReader
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIOImageReader_h_INCLUDED__
--- /dev/null
+/*
+
+*/
+/*! \file
+ \brief Indexed priority queues handled by binary trees.
+*/
+#ifndef __creaImageIOIndexedHeap_h_INCLUDED__
+#define __creaImageIOIndexedHeap_h_INCLUDED__
+
+#include <vector>
+
+namespace creaImageIO
+{
+
+
+
+ template <class T,
+ class Comparator/*=Less<T>*/,
+ class Indexer/*=Index<T> */>
+ class IndexedHeap ;
+ template < class T,
+ class C,
+ class I>
+ std::ostream& operator << (std::ostream&, const IndexedHeap<T,C,I>& );
+
+
+ //template <class T, class Comparator=std::less<T>, class Index=IndexIndex<T> > 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<T,int&> 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 T,
+ class Comparator /*=Less<T>*/,
+ class Indexer /*=Index<T>*/>
+ class IndexedHeap
+ {
+ // friend class SlicedIndexedHeap<T,Comparator,Index>;
+ 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<T> & 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<T> 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
--- /dev/null
+#include <creaImageIOMultiThreadImageReader.h>
+#include <creaImageIOImageReader.h>
+#include <wx/utils.h>
+
+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 <<" )"<<std::endl;
+
+ // Create the threads
+ for (int i=0; i<number_of_threads; i++)
+ {
+ ThreadedImageReader* t = new ThreadedImageReader(this);
+ mThreadedImageReaderList.push_back(t);
+ }
+ mNumberOfThreadedReadersRunning = 0;
+ // Init the queue
+ mQueue.set(mComparator);
+ mQueue.set(mIndexer);
+ //
+ // no thread : alloc self reader
+ if (number_of_threads==0)
+ {
+ mReader = new ImageReader();
+ }
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool MultiThreadImageReader::Start()
+ {
+
+ // std::cout << "#### MultiThreadImageReader::Start()"
+ // <<std::endl;
+ ThreadedImageReaderListType::iterator i;
+ for (i =mThreadedImageReaderList.begin();
+ i!=mThreadedImageReaderList.end();
+ i++)
+ {
+ (*i)->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<<std::endl;
+
+ return true;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void MultiThreadImageReader::Stop()
+ {
+// std::cout << "#### MultiThreadImageReader::Stop()"
+// <<std::endl;
+ // std::cout << "Sending stop order to the threads..."<<std::endl;
+
+ ThreadedImageReaderListType::iterator i;
+ for (i =mThreadedImageReaderList.begin();
+ i!=mThreadedImageReaderList.end();
+ i++)
+ {
+ (*i)->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..."<<std::endl;
+ do
+ {
+ // Sleep a little
+ wxMilliSleep(10);
+ // Lock
+ {
+ wxMutexLocker locker(GetMultiThreadImageReaderUserMutex());
+// std::cout << "#Threads running = "
+// << mNumberOfThreadedReadersRunning<<std::endl;
+ // Break if all readers have stopped
+ if (mNumberOfThreadedReadersRunning <= 0)
+ {
+ break;
+ }
+ }
+ }
+ while (true);
+// std::cout << "All threads stopped : OK "<<std::endl;
+
+ ImageMapType::iterator j;
+ for (j =mImages.begin();
+ j!=mImages.end();
+ ++j)
+
+ {
+ delete j->first;
+ }
+ mImages.clear();
+ }
+ //=====================================================================
+
+ //=====================================================================
+ MultiThreadImageReader::~MultiThreadImageReader()
+ {
+ // std::cout << "#### MultiThreadImageReader::~MultiThreadImageReader()"
+ // <<std::endl;
+ Stop();
+ if (mReader) delete mReader;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void MultiThreadImageReader::UpdateUnloadPriority(ImageToLoadPtr p,
+ int priority)
+ {
+ // not in unload queue : ciao
+ if (p->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<ImageToLoadPtr>(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<ImageToLoadPtr>(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('"<<filename<<"')"
+ // <<std::endl;
+
+ do
+ {
+ wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
+
+ if (mThreadedImageReaderList.size()==0)
+ {
+ ImageToLoad itl(this,filename);
+ ImageMapType::iterator i = mImages.find(&itl);
+ if (i!=mImages.end())
+ {
+ ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(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<ImageToLoadPtr>(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..."<<std::endl;
+
+ // Waiting that it is read
+ int n = 0;
+ do
+ {
+ // std::cout << n++ << std::endl;
+ wxMilliSleep(10);
+ do
+ {
+ // wxMutexLocker lock(mMutex);
+ wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
+ if (mRequestedImage!=0)
+ {
+ return mRequestedImage;
+ }
+ }
+ while (0);
+ }
+ while (true);
+ //
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void MultiThreadImageReader::SignalImageRead(ImageToLoadPtr p,
+ bool purge)
+ {
+ // std::cout << "MultiThreadImageReader::SignalImageRead" <<std::endl;
+ // std::cout << "this="<<this <<std::endl;
+ // std::cout << "user="<<p->GetUser() <<std::endl;
+
+ if ( p->GetUser() == this )
+ GetMultiThreadImageReaderUserMutex().Unlock();
+
+ p->GetUser()->MultiThreadImageReaderSendEvent
+ (p->GetFilename(),
+ MultiThreadImageReaderUser::ImageLoaded,
+ p->GetImage());
+
+ /*
+ AN ATTEMPT TO UNLOAD OLDEST IMAGE IF EXCEEDED A CERTAIN MEMORY QUOTA
+ BUGGY : TO FIX
+ */
+ if (!purge) return;
+
+ // wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
+
+ mUnloadQueue.insert(p);
+ p->GetImage()->UpdateInformation();
+ p->GetImage()->PropagateUpdateExtent();
+ long ImMem = p->GetImage()->GetEstimatedMemorySize();
+ mTotalMem += ImMem;
+ // std::cout << " ==> Total mem = "<<mTotalMem<<" Ko"<<std::endl;
+ while (mTotalMem > 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 !!"
+ // <<std::endl;
+ break;
+
+ }
+ ImageToLoadPtr unload = mUnloadQueue.remove_top();
+ MultiThreadImageReaderUser* user = unload->GetUser();
+
+ if ((user!=0)&&(user!=this))
+ {
+ user->GetMultiThreadImageReaderUserMutex().Lock();
+ }
+
+
+ // std::cout << "'" << unload->GetFilename() << "'" << std::endl;
+ mTotalMem -= unload->GetImage()->GetEstimatedMemorySize();
+ // std::cout << " ==> Total mem = "<<mTotalMem<<" Ko "<<std::endl;
+
+
+ std::string filename = unload->GetFilename();
+ if (unload->Index()>=0)
+ {
+ // std::cout << "still in queue"<<std::endl;
+ }
+ unload->Index() = -1;
+
+
+ ImageMapType::iterator it = mImages.find(unload);
+ if (it!=mImages.end())
+ {
+ mImages.erase(it);
+ }
+ // std::cout << "delete..."<<std::endl;
+ delete unload;
+ // std::cout << "delete ok."<<std::endl;
+
+ if (user!=0)
+ {
+ // std::cout << "unlock..."<<std::endl;
+ user->GetMultiThreadImageReaderUserMutex().Unlock();
+ // std::cout << "event"<<std::endl;
+ user->MultiThreadImageReaderSendEvent
+ (filename,
+ MultiThreadImageReaderUser::ImageUnloaded,
+ 0);
+ // std::cout << "event ok"<<std::endl;
+
+ }
+ }
+
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ int MultiThreadImageReader::GetMaximalPriority()
+ {
+ wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
+ return GetMaximalPriorityWithoutLocking();
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ int MultiThreadImageReader::GetMaximalPriorityWithoutLocking()
+ {
+ long max = 0;
+ if (mQueue.size()>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 "<<GetCurrentId()<<"::Entry()"
+ // << std::endl;
+
+ mMultiThreadImageReader->MultiThreadImageReaderSendEvent
+ ("",
+ MultiThreadImageReaderUser::ThreadedReaderStarted,
+ 0);
+
+ // While was not deleted
+ while (!TestDestroy())
+ {
+ // std::cout << "### Thread "<<GetCurrentId()<<" waiting for image"
+ // << std::endl;
+
+ // Lock the mutex
+ mMultiThreadImageReader->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 "<<GetCurrentId()<<" : reading '"
+ // << i->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<MultiThreadImageReader::ImageToLoadPtr>
+ (it->first);
+ pitl->SetImage(im);
+ mMultiThreadImageReader->SignalImageRead(pitl,true);//i->GetFilename());
+ mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); //mMutex.Unlock();
+
+ // std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
+ // << i->GetFilename() << "' : DONE" << std::endl;
+
+ }
+ else
+ {
+ mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
+ //mMutex.Unlock();
+ // Wait a little to avoid blocking
+ Sleep(10);
+ }
+ };
+ // std::cout << "### Thread "<<GetCurrentId()<<" stopping"
+ // << std::endl;
+
+ return 0;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void ThreadedImageReader::OnExit()
+ {
+ mMultiThreadImageReader->MultiThreadImageReaderSendEvent
+ ("",
+ MultiThreadImageReaderUser::ThreadedReaderStopped,
+ 0);
+ }
+ //=====================================================================
+
+ //=====================================================================
+ vtkImageData* ThreadedImageReader::Read(const std::string& filename)
+ {
+ return mReader.ReadImage(filename);
+ }
+ //=====================================================================
+
+} // namespace creaImageIO
--- /dev/null
+#ifndef __creaImageIOThreadedImageReader_h_INCLUDED__
+#define __creaImageIOThreadedImageReader_h_INCLUDED__
+
+#include <creaImageIOSystem.h>
+#include <creaImageIOImageReader.h>
+#include <creaImageIOIndexedHeap.h>
+#include <map>
+#include <deque>
+#include <wx/thread.h>
+#include <queue>
+
+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 = "<<mImage->GetReferenceCount()<<std::endl;
+ mImage->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<ImageToLoadPtr,vtkImageData*,
+ ImageToLoadPtrFilenameComparator> 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<ImageToLoadPtr,
+ ImageToLoadPtrPriorityComparator,
+ ImageToLoadPtrIndexer> mQueue;
+
+ /// The type of list of threaded readers
+ typedef std::vector<ThreadedImageReader*> 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<ImageToLoadPtr,
+ ImageToLoadPtrInversePriorityComparator,
+ ImageToUnloadPtrIndexer> mUnloadQueue;
+
+ void UpdateUnloadPriority(ImageToLoadPtr p, int priority);
+ long mTotalMem;
+ long mTotalMemMax;
+
+
+ }; // class MultiThreadImageReader
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIOThreadedImageReader_h_INCLUDED__
--- /dev/null
+#include <creaImageIOSQLiteTreeHandler.h>
+
+#include "CppSQLite3.h"
+
+#include <sys/stat.h>
+
+//#include <creaImageIOSQLiteTreeHandlerStructure.h>
+
+//#include <creaImageIOUtilities.h>
+
+//#include <icons/database.xpm>
+
+#include <deque>
+
+#include "wx/wx.h"
+#include <wx/dir.h>
+#include <wx/filename.h>
+
+
+//#include <icons/close.xpm>
+
+#include <creaWx.h>
+#include <creaMessageManager.h>
+using namespace crea;
+
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string/replace.hpp>
+
+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 << "-> '"<<GetName()<< "' - '"
+ << GetFileName()<<"'"<<std::endl;
+ ChildrenListType::const_iterator i;
+ for (i=GetChildrenList().begin(); i!=GetChildrenList().end(); i++)
+ {
+ (*i)->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 '"<<QUER<<"' : "<< e.errorCode() << ":" \
+ << e.errorMessage()<<std::endl; \
+ creaError("SQLite query '"<<QUER<<"' : "<< e.errorCode() << ":" \
+ << e.errorMessage() ); \
+ } \
+
+ //=====================================================================
+
+ //=====================================================================
+#define UPDATEDB(UP) \
+ try \
+ { \
+ mDB->execDML(UP.c_str()); \
+ } \
+ catch (CppSQLite3Exception& e) \
+ { \
+ std::cout << "SQLite update '"<<UP<<"' Error : "<< e.errorCode() << ":" \
+ << e.errorMessage()<<std::endl; \
+ creaError("SQLite update '"<<UP<<"' Error : "<< e.errorCode() << ":" \
+ << e.errorMessage() ); \
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Open(bool writable)
+ {
+ // std::cout << "***> SQLiteTreeHandler::Open('"<<GetFileName()<<"')"<<std::endl;
+ SetWritable(writable);
+ return DBOpen();
+ }
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Create(bool writable)
+ {
+ // std::cout << "***> SQLiteTreeHandler::New('"<<GetFileName()<<"')"<<std::endl;
+ SetWritable(writable);
+ return DBCreate();
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Close()
+ {
+ return false;
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Destroy()
+ {
+ return false;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ unsigned int SQLiteTreeHandler::GetNumberOfChildren(tree::Node* n)
+ {
+ return 0;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ int SQLiteTreeHandler::LoadChildren(tree::Node* parent, int maxlevel)
+ {
+ return 0;
+ }
+ //=====================================================================
+
+
+
+
+ //=====================================================================
+ void SQLiteTreeHandler::UnLoad(tree::Node* n)
+ {
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ int SQLiteTreeHandler::AddBranch( const std::map<std::string,std::string>& attr )
+ {
+ return -1;
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Remove(tree::Node*)
+ {
+ return false;
+ }
+ //=====================================================================
+
+
+
+
+
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::DBOpen()
+ {
+ // std::cout << "### Opening SQLite database '"<<GetFileName()<<std::endl;
+ // OPENING FILE
+ if (!boost::filesystem::exists(GetFileName()))
+ {
+ return false;
+ }
+
+ try
+ {
+ mDB->open(GetFileName().c_str());
+ }
+ catch (CppSQLite3Exception& e)
+ {
+ std::cerr << "Opening '"<<GetFileName()<<"' : "
+ << e.errorCode() << ":"
+ << e.errorMessage() << std::endl;
+ return false;
+ }
+ // TESTING STRUCTURE VALIDITY
+ if (!DBStructureIsValid())
+ {
+ std::cerr << "Opening '"<<GetFileName()<<"' : "
+ << " invalid database structure" << std::endl;
+ return false;
+ }
+ // IMPORTING NODE TYPE DESCRIPTIONS
+ DBImportTreeDescription();
+ return true;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool SQLiteTreeHandler::DBCreate()
+ {
+ // std::cout << "### Creating SQLite database '"<<GetFileName()<<std::endl;
+
+ if (boost::filesystem::exists(GetFileName()))
+ {
+ creaMessage("Gimmick!",1,
+ "[Gimmick!] !! ERROR '"<<GetFileName()<<"' : "
+ << "file already exists"<<std::endl);
+ return false;
+ }
+
+ // OPENING
+ try
+ {
+ mDB->open(GetFileName().c_str());
+ }
+ catch (CppSQLite3Exception& e)
+ {
+ creaMessage("Gimmick!",1,
+ "[Gimmick!] !! ERROR '"
+ << e.errorCode() << ":"
+ << e.errorMessage() <<std::endl);
+ return false;
+ }
+
+ // CREATING TABLES
+ try
+ {
+ std::string command;
+ // Level 0
+ command = "create table ";
+ command += GetTree().GetLevelDescriptor(0).GetName();
+ command += "\n(\nID INTEGER PRIMARY KEY,\n";
+ AppendAttributesSQLDefinition(0,command);
+ command += "\n)";
+ UPDATEDB(command);
+
+ // Iterate the other Levels
+ for (int l=1; l<GetTree().GetNumberOfLevels(); ++l)
+ {
+ command = "create table ";
+ command += GetTree().GetLevelDescriptor(l).GetName();
+ command += "\n(\nID INTEGER PRIMARY KEY,\nPARENT_ID int not null,\n";
+ AppendAttributesSQLDefinition(l,command);
+ command +=",\n";
+ command +="constraint FK_PARENT foreign key (PARENT_ID) references PATIENT(ID) on delete restrict on update restrict\n)";
+ UPDATEDB(command);
+ }
+
+ // Create tables *_ATTRIBUTES
+ for (int l=1; l<GetTree().GetNumberOfLevels(); ++l)
+ {
+ command = "create table ";
+ command += GetTree().GetLevelDescriptor(l).GetName();
+ command += "_ATTRIBUTES\n(\n";
+ command += "Key text,\n";
+ command += "Group int,\n";
+ command += "Element int,\n";
+ command += "Name text,\n";
+ command += "Flags int\n";
+ command += "\n)";
+ UPDATEDB(command);
+ }
+ // Fill the tables *_ATTRIBUTES
+ DBExportTreeDescription();
+
+ }
+ catch (std::exception)
+ {
+ return false;
+ }
+ // LG : TEST
+ if (DBStructureIsValid())
+ {
+ // std::cout << "*** DONE ***"<<std::endl;
+ // mDBLoaded = true;
+ GetTree().SetChildrenLoaded(true);
+ return true;
+
+ }
+ else
+ {
+ // std::cout << "*** AAAARRRRGGG ***"<<std::endl;
+
+ return false;
+ }
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void SQLiteTreeHandler::AppendAttributesSQLDefinition(int level,
+ std::string& s)
+ {
+ /*
+ std::vector<std::string*> 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<std::string*>::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()"<<std::endl;
+ for (Node::Type type=Node::Patient;
+ type<=Node::Image;
+ type++)
+ {
+ NodeTypeDescription::FieldDescriptionMapType::iterator i;
+ for (i = GetNodeTypeDescription(type).GetFieldDescriptionMap().begin();
+ i!= GetNodeTypeDescription(type).GetFieldDescriptionMap().end();
+ i++)
+ {
+
+ std::stringstream insert;
+ insert << "INSERT INTO "
+ << std::string(SQLiteTreeHandlerStructure::Table(type))
+ << "_FIELDS (Key,DicomGroup,DicomElement,Name,Flags) "
+ << "VALUES ('"
+ << (*i).second.GetKey() << "',"
+ << (*i).second.GetGroup() << ","
+ << (*i).second.GetElement() << ",'"
+ << (*i).second.GetName() << "',"
+ << (*i).second.GetFlags() << ");";
+
+ // std::cout << "** SQL = '"<<insert.str()<<"'"<<std::endl;
+
+ UPDATEDB(insert.str());
+ }
+ }
+ */
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void SQLiteTreeHandler::DBImportTreeDescription()
+ {
+ // std::cout<<"ImportNodeTypeDescriptionsFromDB()"<<std::endl;
+ /*
+ for (Node::Type type=Node::Patient;
+ type<=Node::Image;
+ type++)
+ {
+ std::string query = "SELECT * FROM ";
+ query += SQLiteTreeHandlerStructure::Table(type);
+ query += "_FIELDS";
+
+ // std::cout << "** SQL = '"<<query<<"'"<<std::endl;
+
+ CppSQLite3Query q;
+ QUERYDB(query,q);
+
+
+ while (!q.eof())
+ {
+ Field::Description d(q.getStringField(0), // Key
+ q.getIntField(1), // Group
+ q.getIntField(2), // Element
+ q.getStringField(3), // Name
+ q.getIntField(4) // Flags
+ );
+ GetNodeTypeDescription(type).GetFieldDescriptionMap()[d.key] = d;
+ // std::cout << d << std::endl;
+ q.nextRow();
+ }
+ }
+ */
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::DBStructureIsValid()
+ {
+ bool success = false; //true;
+
+ // TO DO : TABLE WHICH STORES THE LEVELS
+ /*
+
+ for (int i = SQLiteTreeHandlerStructure::TableBegin();
+ i != SQLiteTreeHandlerStructure::TableEnd();++i)
+ {
+ bool ok = mDB->tableExists(SQLiteTreeHandlerStructure::Table(i));
+ if (ok)
+ {
+ // std::cout << "** Table "<<SQLiteTreeHandlerStructure::Table(i)
+ // <<" exists"<<std::endl;
+ // TO DO : TEST MANDATORY FIELDS EXIST
+ }
+ else
+ {
+ // std::cout << "** Table "<<SQLiteTreeHandlerStructure::Table(i)
+ // <<" does *NOT* exist"<<std::endl;
+ success = false;
+ }
+ }
+ */
+ return success;
+ }
+ //=====================================================================
+
+ /*
+ //=====================================================================
+ int SQLiteTreeHandler::DBLoadChildren(Node* parent,
+ int maxlevel)
+ {
+
+ // std::cout << "SQLiteTreeHandler::DBLoadChildren("<<parent<<","<<maxlevel
+ // << ")"<<std::endl;
+ int nbloaded = 0;
+ if (parent == GetTree()) { parent = 0; }
+ Node* xparent = parent;
+ if ( xparent==0 ) xparent = GetTree();
+ if ( xparent->ChildrenLoaded() )
+ {
+ // std::cout << "--> Children already loaded"<<std::endl;
+ return nbloaded;
+ }
+ if ( xparent->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 = '"<<query<<"'"<<std::endl;
+
+ CppSQLite3Query q;
+ QUERYDB(query,q);
+
+ while (!q.eof())
+ {
+ nbloaded++;
+ Node* n = new Node(type,
+ this,xparent);
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ n->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('"<<alien_node->GetLabel()
+ // <<"')"<<std::endl;
+
+ // if (!ChildrenLoaded()) DBLoadChildren(this,Node::Database);
+
+ // Find parent
+ Node* parent;
+ std::string parent_id;
+ parent = DBGetOrCreateParent(alien_node,parent_id,summary);
+
+ // Insert
+ DBRecursiveGetOrCreateNode(alien_node,parent,parent_id,summary);
+ return true;
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ Node* SQLiteTreeHandler::DBGetOrCreateParent(Node* alien_node,
+ std::string& parent_id,
+ UpdateSummary& summary)
+ {
+ // std::cout << "DBGetOrCreateParent '" << alien_node->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<Node*> chain;
+ Node* cur = alien_node->GetParent();
+ for (int type=Node::Patient;
+ type<alien_node->GetType();++type)
+ {
+ chain.push_front(cur);
+ cur = cur->GetParent();
+ }
+
+ // create the nodes if do not exist
+ std::deque<Node*>::iterator i;
+ for (i=chain.begin();i!=chain.end();++i)
+ {
+ // std::cout << " cur = '"<<(*i)->GetLabel()<<"'"<<std::endl;
+ // std::string cur_id = DBGetNodeId(*i,parent_id);
+ // if (cur_id.size()==0)
+ // {
+ // Node does not exist : create it
+ std::string cur_id;
+ parent = DBGetOrCreateNode(*i,
+ parent,
+ parent_id,
+ cur_id,
+ summary
+ );
+ DBLoadChildren(parent,parent->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('"
+ // <<alien_node->GetLabel()
+ // <<"','"<<parent<<"','"<<parent_id<<"')"<<std::endl;
+ if (parent != 0)
+ {
+ // std::cout << " -- Parent = '"<<parent->GetLabel()<<"'"<<std::endl;
+ }
+ std::string new_id;
+ Node* new_node = DBGetOrCreateNode(alien_node,
+ parent,
+ parent_id,
+ new_id,
+ summary);
+ Node::ChildrenListType::iterator i;
+ for (i = alien_node->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('"<<alien_node->GetLabel()<<"','"
+ // << internal_parent << "','"<< parent_id<<"')"<<std::endl;
+ if (internal_parent != 0)
+ {
+ // std::cout << " -- Parent = '"<<internal_parent->GetLabel()<<"'"<<std::endl;
+ }
+ // Node Exists ? return it
+ // First try among children of internal parent
+ Node* node = GetChildrenLike(internal_parent,alien_node);
+ if (node>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 = '"<<insert<<"'"<<std::endl;
+ UPDATEDB(insert);
+ // std::cout << "** SQL OK"<<std::endl;
+
+ // Store DB id of newly created node;
+ long lastrow = mDB->lastRowId();
+ std::stringstream ri;
+ ri << mDB->lastRowId();
+ node_id = ri.str();
+ // std::cout << "LastRowId='"<<mDB->lastRowId()<<"' vs '"<<created_id<<"'"<<std::endl;
+
+ node->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 ("<<ti.type<<","<<ti.id<<")"<<std::endl;
+ //
+ msw[2].Pause();
+
+ if (node->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;
+ j<SQLiteTreeHandlerStructure::NbQueryFields(type);
+ j++)
+ {
+ if (
+ alien_node->GetFieldValue(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('"<<node->GetLabel()
+ // <<"','"<<parent_id<<"')"
+ // <<std::endl;
+ msw[2].Resume();
+ int type = node->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;i<SQLiteTreeHandlerStructure::NbQueryFields(type);i++)
+ {
+ where += SQLiteTreeHandlerStructure::QueryField(type,i).key + "='"
+ + node->GetFieldValue(SQLiteTreeHandlerStructure::QueryField(type,i).key) + "' ";
+ if (i<SQLiteTreeHandlerStructure::NbQueryFields(type)-1)
+ where += "AND ";
+ }
+
+ std::string query = "SELECT ID FROM " + table + " " + where + ";";
+ // std::cout << "** SQL = '"<<query<<"'"<<std::endl;
+ CppSQLite3Query q;
+ QUERYDB(query,q);
+
+ if (!q.eof())
+ {
+
+ // std::cout << " - Node exists " << std::endl;
+ std::string id = q.getStringField(0);
+ // std::cout << " id = '"<<id<<"'"<<std::endl;
+ msw[2].Pause();
+ return id;
+ }
+ msw[2].Pause();
+ return "";
+ }
+ //=====================================================================
+
+
+
+ //=====================================================================
+ Node* SQLiteTreeHandler::GetNodeFromTypeId(Node::Type type,
+ const std::string& id)
+ {
+ // std::cout << "GetNodeFromTypeId("<<type<<","<<id<<")"<<std::endl;
+
+ TypeId ti;
+ ti.type = type;
+ ti.id = id;
+
+ TypeIdToNodeMapType::iterator i = mTypeIdToNodeMap.find(ti);
+ if (i == mTypeIdToNodeMap.end())
+ {
+
+ std::cout << "Internal error : mTypeIdToNodeMap does not contain key"
+ << std::endl;
+ creaError("Internal error : mTypeIdToNodeMap does not contain key");
+ // }
+ }
+
+ // std::cout << " ** Node = "<<i->second<<std::endl;
+ return i->second;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Remove(Node* node)
+ {
+ DBRecursiveRemoveNode(node);
+
+ // std::cout << "DELETE"<<std::endl;
+ if (node->GetParent())
+ {
+ node->GetParent()->RemoveChildrenFromList(node);
+ }
+ delete node;
+ // std::cout << "DELETE OK"<<std::endl;
+ return true;
+ }
+ //========================================================================
+
+ //=====================================================================
+ void SQLiteTreeHandler::DBRecursiveRemoveNode(Node* node)
+ {
+ // std::cout << "SQLiteTreeHandler::DBRecursiveRemoveNode('"
+ // <<node->GetLabel()<<"')"<<std::endl;
+
+ std::string query = "DELETE FROM ";
+ query += SQLiteTreeHandlerStructure::Table(node->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) <<std::endl;
+
+ return q.getIntField(0);
+ }
+ //=====================================================================
+
+ //========================================================================
+ std::string& format_sql2(std::string& str)
+ {
+ // quote must be doubled
+ // crea::Utils::Replace( str, "'", "''" );
+ boost::algorithm::replace_all(str,"'","''");
+ // Found strange strings which contained NULL char INSIDE string
+ int i,size=str.size();
+ for (i=0;i<size;++i)
+ {
+ if (str[i]==0)
+ {
+ str = str.substr(0,i);
+ break;
+ }
+ }
+ // if (i<str.size())
+ return str;
+ }
+ //========================================================================
+
+ //=====================================================================
+ void SQLiteTreeHandler::BuildSQLFieldsValues(Node* n,
+ std::string& str)
+ {
+ // std::cout << "BuildSQLFieldsValues('"<<n->GetLabel()<<"')"<<std::endl;
+
+ std::string atts="";
+ std::string values="";
+ Node::FieldValueMapType::iterator i;
+ for (i = n->GetFieldValueMap().begin();
+ i != n->GetFieldValueMap().end();
+ i++)
+ {
+ if (i->first=="ID")
+ {
+ continue;
+ }
+ // std::cout << "("<<i->first<<","<<i->second<<")"<<std::endl;
+ atts += "'" + i->first + "'";
+ values += "'" + format_sql2(i->second) + "'";
+ atts += ",";
+ values += ",";
+ }
+ atts[atts.size()-1]=' ';
+ values[values.size()-1]=' ';
+
+ str = "("+atts+") VALUES ("+values+")";
+
+ }
+ //=====================================================================
+*/
+
+
+
+} // namespace creaImageIO
--- /dev/null
+#ifndef __creaImageIOSQLiteTreeHandler_h_INCLUDED__
+#define __creaImageIOSQLiteTreeHandler_h_INCLUDED__
+
+#include <creaImageIOTreeHandler.h>
+
+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<std::string,std::string>& 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<o.type) ||
+ ((type==o.type)&&id<o.id));
+ }
+ };
+
+ typedef std::map<TypeId,tree::Node*> TypeIdToNodeMapType;
+ TypeIdToNodeMapType mTypeIdToNodeMap;
+ };
+ // EO class SQLiteTreeHandler
+ //=======================================================================
+
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
+
--- /dev/null
+#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
+
--- /dev/null
+#include <creaImageIOTree.h>
+
+
+namespace creaImageIO
+{
+ namespace tree
+ {
+
+ Tree::Tree()
+ : Node(0)
+ {
+
+ }
+
+ Tree::~Tree()
+ {
+
+ }
+
+
+ }
+}
--- /dev/null
+#ifndef __creaImageIOTree_h_INCLUDED__
+#define __creaImageIOTree_h_INCLUDED__
+
+#include <creaImageIOTreeNode.h>
+
+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
--- /dev/null
+#include <creaImageIOTreeAttributeDescriptor.h>
+
+#include <gdcmGlobal.h>
+#include <gdcmDictSet.h>
+
+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
--- /dev/null
+#ifndef __creaImageIOTreeAttributeDescriptor_h_INCLUDED__
+#define __creaImageIOTreeAttributeDescriptor_h_INCLUDED__
+
+#include <string>
+//#include <iostream>
+
+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__
--- /dev/null
+#include <creaImageIOTreeComparators.h>
--- /dev/null
+#ifndef __creaImageIOTreeNodeComparators_h_INCLUDED__
+#define __creaImageIOTreeNodeComparators_h_INCLUDED__
+
+#include <vector>
+#include <iostream>
+
+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<Comparator*>::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<Comparator*> 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__
--- /dev/null
+#include <creaImageIOTreeDescriptor.h>
+
+
+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<std::string,std::string>& 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()]="";
+ }
+ }
+ }
+ //==================================================================
+
+ }
+}
--- /dev/null
+#ifndef __creaImageIOTreeDescriptor_h_INCLUDED__
+#define __creaImageIOTreeDescriptor_h_INCLUDED__
+
+#include <creaImageIOTreeLevelDescriptor.h>
+#include <map>
+
+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<std::string,std::string>&) const;
+
+ /// The type of LevelDescriptor container
+ typedef std::vector<LevelDescriptor> 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
--- /dev/null
+#ifndef __creaImageIOTreeHandler_h_INCLUDED__
+#define __creaImageIOTreeHandler_h_INCLUDED__
+
+#include <creaImageIOTree.h>
+
+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<std::string,std::string>& 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
+
--- /dev/null
+#include <creaImageIOTreeLevelDescriptor.h>
+
+namespace creaImageIO
+{
+ namespace tree
+ {
+
+ }
+}
+
--- /dev/null
+#ifndef __creaImageIOTreeLevelDescriptor_h_INCLUDED__
+#define __creaImageIOTreeLevelDescriptor_h_INCLUDED__
+
+#include <creaImageIOTreeAttributeDescriptor.h>
+#include <vector>
+
+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<AttributeDescriptor> 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
--- /dev/null
+#include <creaImageIOTreeNode.h>
+#include <creaImageIOTree.h>
+#include <creaMessageManager.h>
+#include <algorithm>
+
+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<<std::endl;
+ // std::cout << "mFieldValueMap="<<(void*)(&mFieldValueMap)<<std::endl;
+ AttributeMapType::const_iterator i = mAttributeMap.find(k);
+ if (i == mAttributeMap.end())
+ {
+ static std::string def("");
+ return def;
+ // CREAIMAGEIO_ERROR("DicomNode::GetFieldValue : no field with key '"<<k<<"'");
+ }
+ return i->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 '"
+ <<k<<"'"<<std::endl;
+ creaError( "[Gimmick!] Node::SetAttribute : no attribute with name '"
+ <<k<<"'");
+ }
+ i->second = v;
+ }
+ //=============================================================
+
+
+ }
+
+}
+
--- /dev/null
+#ifndef __creaImageIOTreeNode_h_INCLUDED__
+#define __creaImageIOTreeNode_h_INCLUDED__
+
+#include <creaImageIOTreeDescriptor.h>
+#include <creaImageIOTreeComparators.h>
+#include <vector>
+#include <map>
+
+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<Node*> 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<std::string,std::string> 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<class T> T GetData() const
+ { if (mData!=0) return dynamic_cast<T>(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__