]> Creatis software - creaImageIO.git/commitdiff
Starting version 2
authorguigues <guigues>
Mon, 9 Feb 2009 10:09:24 +0000 (10:09 +0000)
committerguigues <guigues>
Mon, 9 Feb 2009 10:09:24 +0000 (10:09 +0000)
40 files changed:
CMakeLists.txt
appli/CMakeLists.txt
appli/gimmick/CMakeLists.txt [new file with mode: 0644]
appli/gimmick/main.cxx [new file with mode: 0644]
src/CMakeLists.txt
src/creaImageIODicomNode.cpp
src/creaImageIODicomNodeComparators.cpp
src/creaImageIOWxGimmick.cpp
src/creaImageIOWxGimmick.h
src2/AdditionalUsecreaImageIO2.cmake.in [new file with mode: 0644]
src2/AdditionalcreaImageIO2Config.cmake.in [new file with mode: 0644]
src2/CMakeLists.txt [new file with mode: 0644]
src2/CppSQLite3.cpp [new file with mode: 0644]
src2/CppSQLite3.h [new file with mode: 0644]
src2/README.txt [new file with mode: 0644]
src2/creaImageIOGimmick.cpp [new file with mode: 0644]
src2/creaImageIOGimmick.h [new file with mode: 0644]
src2/creaImageIOImageFinder.cpp [new file with mode: 0644]
src2/creaImageIOImageFinder.h [new file with mode: 0644]
src2/creaImageIOImageReader.cpp [new file with mode: 0644]
src2/creaImageIOImageReader.h [new file with mode: 0644]
src2/creaImageIOIndexedHeap.h [new file with mode: 0644]
src2/creaImageIOMultiThreadImageReader.cpp [new file with mode: 0644]
src2/creaImageIOMultiThreadImageReader.h [new file with mode: 0644]
src2/creaImageIOSQLiteTreeHandler.cpp [new file with mode: 0644]
src2/creaImageIOSQLiteTreeHandler.h [new file with mode: 0644]
src2/creaImageIOSystem.h [new file with mode: 0644]
src2/creaImageIOTree.cpp [new file with mode: 0644]
src2/creaImageIOTree.h [new file with mode: 0644]
src2/creaImageIOTreeAttributeDescriptor.cpp [new file with mode: 0644]
src2/creaImageIOTreeAttributeDescriptor.h [new file with mode: 0644]
src2/creaImageIOTreeComparators.cpp [new file with mode: 0644]
src2/creaImageIOTreeComparators.h [new file with mode: 0644]
src2/creaImageIOTreeDescriptor.cpp [new file with mode: 0644]
src2/creaImageIOTreeDescriptor.h [new file with mode: 0644]
src2/creaImageIOTreeHandler.h [new file with mode: 0644]
src2/creaImageIOTreeLevelDescriptor.cpp [new file with mode: 0644]
src2/creaImageIOTreeLevelDescriptor.h [new file with mode: 0644]
src2/creaImageIOTreeNode.cpp [new file with mode: 0644]
src2/creaImageIOTreeNode.h [new file with mode: 0644]

index 221a8e48974ebaf1ca215b55081b056e4b9f0d67..8a97a2ebaf01ccac81131fddc3941779b70fcd2f 100644 (file)
@@ -30,18 +30,30 @@ MARK_AS_ADVANCED(
   LIBRARY_OUTPUT_PATH
   )
 
-# Includes 
-INCLUDE_DIRECTORIES(
-#  ${PROJECT_BINARY_DIR}
-  ${PROJECT_SOURCE_DIR}/src
-  )
-
 IF(WIN32)
 INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/win32)
 LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/win32)
 ENDIF(WIN32)
 
-SUBDIRS(src)
+
+OPTION(BUILD_V2 "Build Version 2 ?" OFF) 
+
+IF (BUILD_V2)
+  INCLUDE_DIRECTORIES(
+    ${PROJECT_BINARY_DIR}
+    ${PROJECT_SOURCE_DIR}/src2
+    )
+  SUBDIRS(src2)
+ELSE (BUILD_V2)
+  INCLUDE_DIRECTORIES(
+    ${PROJECT_BINARY_DIR}
+    ${PROJECT_SOURCE_DIR}/src
+    )
+  SUBDIRS(src)
+ENDIF (BUILD_V2)
+
+
+#
 SUBDIRS(appli)
 SUBDIRS(bbtk)
 SUBDIRS(install)
index 95cdfdea5fe7f80c4cab671f166aa473c04a68c6..cdd44c7430634fcff5a69020397105134c4bb4c8 100644 (file)
@@ -1 +1,9 @@
-SUBDIRS(TestWxGimmickDialog)
+IF (BUILD_V2)
+  SUBDIRS(gimmick)
+ELSE (BUILD_V2)
+  SUBDIRS(TestWxGimmickDialog)
+ENDIF (BUILD_V2)
+
+
+
+
diff --git a/appli/gimmick/CMakeLists.txt b/appli/gimmick/CMakeLists.txt
new file mode 100644 (file)
index 0000000..20b6c9d
--- /dev/null
@@ -0,0 +1,11 @@
+
+IF(WIN32)
+  ADD_EXECUTABLE(gimmick main)  
+  SET_TARGET_PROPERTIES(gimmick PROPERTIES LINK_FLAGS /subsystem:console )
+ELSE(WIN32)
+  ADD_EXECUTABLE(gimmick main)
+ENDIF(WIN32)
+
+TARGET_LINK_LIBRARIES( gimmick creaImageIO2)
+
+INSTALL_TARGETS(/bin/ gimmick )
diff --git a/appli/gimmick/main.cxx b/appli/gimmick/main.cxx
new file mode 100644 (file)
index 0000000..7f21a16
--- /dev/null
@@ -0,0 +1,9 @@
+#include <creaImageIOGimmick.h>
+
+int main(int argc, char* argv[])
+{
+  creaImageIO::Gimmick g;
+  if (!g.Initialize()) return 1;
+  if (!g.Finalize()) return 1;
+  return 0;
+}
index 1aff598a837784e55f34fa594fb3a26d4f956b79..13446594fd16d6097f738ac2fea292258d3fdf5b 100644 (file)
@@ -24,7 +24,6 @@ SET( SRCS
 
   creaImageIOWxGimmickDialog
 
-
 #  creaImageIOWxDicomDatabaseTreeView
 #  creaImageIOWxDicomDatabaseTreeViewSettings
 #  creaImageIOWxDicomNodeFieldsView
index 1ddbd6f4809bf736ae5ac8dfa50c1389f14f9c6c..04879c8477346f0e74c1bbeedf6cd91361e05ff6 100644 (file)
@@ -189,7 +189,7 @@ namespace creaImageIO
   //=============================================================
   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++)
index 7d34a0bbda4243b833b95c2207eb4884c1bacd86..c19b80c8486d1c9696620787c1744b9463a45268 100644 (file)
@@ -21,8 +21,8 @@ namespace creaImageIO
   { 
     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);
   }
   //===================================================================
index ef0439de64ce04901a63d815706b86e6975152b0..74bebb7677099185ecabed5e3d4a4780b91daed8 100644 (file)
@@ -63,6 +63,8 @@ namespace creaImageIO
       PopUp_Settings = 501,
       PopUp_About    = 502,
       PopUp_User = WxGimmick::UserMenuFirstId,
+      PopUp_SaveAs = 701,
+      PopUp_AddToFavorites = 702
     };
   //================================================================
 
@@ -931,11 +933,13 @@ namespace creaImageIO
   {
     wxBusyCursor busy;
     //    std::cout << "WxGimmick : Reading config"<<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())
@@ -1024,6 +1028,13 @@ namespace creaImageIO
   }
   //================================================================
 
+  //================================================================
+  void WxGimmick::LoadOrCreateFavoritesDatabase()
+  {
+    // TODO
+  }
+  //================================================================
+
   /*
   //================================================================
   void  WxGimmick::OnClose(wxCloseEvent& event)
index 9673b5cca0f4086415ff0857d705a68691f15f75..75e969ef453c2ebcecb61cfd4032284716770526 100644 (file)
@@ -46,58 +46,96 @@ namespace creaImageIO
                                       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);
@@ -118,6 +156,7 @@ namespace creaImageIO
     int mCurrentSelectionImageSize[4];
 
     DicomDatabaseListType mDicomDatabaseList;
+    DicomDatabase* mFavoriteDatabase;
 
     wxTreeListCtrl* mTreeListCtrl;
     wxTreeItemId mTreeRootId;
@@ -239,6 +278,7 @@ namespace creaImageIO
     void OnPopUpAddFile(wxCommandEvent& event);
     void OnPopUpAddRawFile(wxCommandEvent& event);
     void OnPopUpAddDirectory(wxCommandEvent& event);
+    void OnPopUpAddToFavorites(wxCommandEvent& event);
     void OnPopUpRemove(wxCommandEvent& event);
     void OnPopUpSettings(wxCommandEvent& event);
     void OnPopUpAbout(wxCommandEvent& event);
diff --git a/src2/AdditionalUsecreaImageIO2.cmake.in b/src2/AdditionalUsecreaImageIO2.cmake.in
new file mode 100644 (file)
index 0000000..bdf20d7
--- /dev/null
@@ -0,0 +1,2 @@
+INCLUDE(${crea_USE_FILE})
+
diff --git a/src2/AdditionalcreaImageIO2Config.cmake.in b/src2/AdditionalcreaImageIO2Config.cmake.in
new file mode 100644 (file)
index 0000000..35ae10d
--- /dev/null
@@ -0,0 +1,2 @@
+# We have to find crea
+FIND_PACKAGE(crea REQUIRED)
\ No newline at end of file
diff --git a/src2/CMakeLists.txt b/src2/CMakeLists.txt
new file mode 100644 (file)
index 0000000..41e2ed0
--- /dev/null
@@ -0,0 +1,137 @@
+SET(LIBRARY_NAME creaImageIO2)
+
+SET( SRCS
+  # SQLite 
+  CppSQLite3
+  
+  # wxTreeListCtrl
+#  treelistctrl.cpp
+
+
+  # Attributed tree data structure
+  creaImageIOTree
+  creaImageIOTreeNode
+  creaImageIOTreeDescriptor
+  creaImageIOTreeLevelDescriptor
+  creaImageIOTreeAttributeDescriptor
+  creaImageIOTreeComparators
+
+  # Image readers
+  creaImageIOImageReader
+  creaImageIOMultiThreadImageReader
+
+  # Tree Handlers
+  creaImageIOTreeHandler
+  creaImageIOSQLiteTreeHandler
+
+  # 
+  creaImageIOImageFinder
+  
+  # 
+  creaImageIOGimmick
+#  creaImageIODicomNode
+#  creaImageIODicomNodeComparators
+#  creaImageIODicomNodeTypeDescription
+#  creaImageIODicomDatabaseStructure
+#  creaImageIODicomDatabase
+#  creaImageIOField
+
+  # The Gimmick! widgets
+#  creaImageIOWxGimmick
+#  creaImageIOWxGimmickSettings
+#  creaImageIOWxGimmickFieldsView
+
+#  creaImageIOWxGimmickDialog
+
+)
+
+
+
+OPTION(${LIBRARY_NAME}_BUILD_SHARED 
+  "Build ${LIBRARY_NAME} as a shared library (dynamic) ?" ON)
+IF (${LIBRARY_NAME}_BUILD_SHARED)
+  SET(CREAIMAGEIO_BUILD_SHARED SHARED)
+  crea_DEFINE(CREAIMAGEIO_BUILD_SHARED)
+ENDIF(${LIBRARY_NAME}_BUILD_SHARED)
+
+crea_DEFINE(CREAIMAGEIO_EXPORT_SYMBOLS)
+
+ADD_LIBRARY(${LIBRARY_NAME} ${CREAIMAGEIO_BUILD_SHARED} ${SRCS})
+
+
+
+TARGET_LINK_LIBRARIES(${LIBRARY_NAME} 
+  ${crea_LIBRARIES}
+  ${WXWIDGETS_LIBRARIES}
+  ${VTK_LIBRARIES}
+  ${GDCM_LIBRARIES}
+  ${BOOST_LIBRARIES}
+  sqlite3)
+
+#----------------------------------------------------------------------------
+# INSTALLS LIBRARY
+FILE(GLOB HEADERS "*.h")
+INSTALL(
+  FILES ${HEADERS}
+  DESTINATION include/${LIBRARY_NAME}
+  )
+IF (WIN32)
+  SET(CREAIMAGEIO_LIB_PATH bin)
+ELSE (WIN32)
+  SET(CREAIMAGEIO_LIB_PATH lib)
+ENDIF(WIN32)
+
+INSTALL(
+  TARGETS ${LIBRARY_NAME} 
+  DESTINATION ${CREAIMAGEIO_LIB_PATH})
+
+  
+   # Sets the settings for macro CREA_ADVANCED_INSTALL_LIBRARY_FOR_CMAKE
+  SET(${LIBRARY_NAME}_INSTALL_FOLDER ${LIBRARY_NAME})
+  SET(${LIBRARY_NAME}_LIBRARIES ${LIBRARY_NAME})
+  
+  FILE(RELATIVE_PATH 
+    ${LIBRARY_NAME}_BUILD_TREE_RELATIVE_INCLUDE_PATHS 
+    ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+    )
+  IF ( ${PROJECT_BINARY_DIR} STREQUAL ${EXECUTABLE_OUTPUT_PATH} )
+    SET(CILFC_EXECUTABLE_OUTPUT_REL_PATH ".")
+  ELSE ( ${PROJECT_BINARY_DIR} STREQUAL ${EXECUTABLE_OUTPUT_PATH} )
+    FILE(RELATIVE_PATH 
+      CILFC_EXECUTABLE_OUTPUT_REL_PATH
+      ${PROJECT_BINARY_DIR} ${EXECUTABLE_OUTPUT_PATH})
+  ENDIF ( ${PROJECT_BINARY_DIR} STREQUAL ${EXECUTABLE_OUTPUT_PATH} )
+
+  IF(UNIX)
+    SET(${LIBRARY_NAME}_BUILD_TREE_RELATIVE_LIBRARY_PATHS 
+      ${CILFC_EXECUTABLE_OUTPUT_REL_PATH})
+    SET(${LIBRARY_NAME}_INSTALL_TREE_RELATIVE_LIBRARY_PATHS lib)
+  ELSE(UNIX)
+    SET(${LIBRARY_NAME}_BUILD_TREE_RELATIVE_LIBRARY_PATHS 
+      ${CILFC_EXECUTABLE_OUTPUT_REL_PATH})
+    SET(${LIBRARY_NAME}_INSTALL_TREE_RELATIVE_LIBRARY_PATHS bin)
+  ENDIF(UNIX)
+  SET(${LIBRARY_NAME}_INSTALL_TREE_RELATIVE_INCLUDE_PATHS include/${LIBRARY_NAME})
+  
+  SET(${LIBRARY_NAME}_HAS_ADDITIONAL_CONFIG_FILE TRUE)
+SET(${LIBRARY_NAME}_ADDITIONAL_CONFIG_FILE 
+  ${PROJECT_SOURCE_DIR}/src2/Additional${LIBRARY_NAME}Config.cmake.in)
+SET(${LIBRARY_NAME}_ADDITIONAL_USE_FILE 
+  ${PROJECT_SOURCE_DIR}/src2/AdditionalUse${LIBRARY_NAME}.cmake.in)
+
+  # Invoke the advanced macro
+  CREA_ADVANCED_INSTALL_LIBRARY_FOR_CMAKE(${LIBRARY_NAME})
+
+  
+#CREA_INSTALL_LIBRARY_FOR_CMAKE(${LIBRARY_NAME})
+#-----------------------------------------------------------------------------
+
+OPTION( BUILD_DOXYGEN_DOC "Build doxygen doc ?" OFF)
+IF(BUILD_DOXYGEN_DOC)
+  SUBDIRS(doxygen)
+ENDIF(BUILD_DOXYGEN_DOC)
+
+INCLUDE_DIRECTORIES(
+#  ${PROJECT_BINARY_DIR}
+  ${PROJECT_SOURCE_DIR}/src2
+  )
diff --git a/src2/CppSQLite3.cpp b/src2/CppSQLite3.cpp
new file mode 100644 (file)
index 0000000..3d074d5
--- /dev/null
@@ -0,0 +1,1500 @@
+////////////////////////////////////////////////////////////////////////////////\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
diff --git a/src2/CppSQLite3.h b/src2/CppSQLite3.h
new file mode 100644 (file)
index 0000000..c45c9cb
--- /dev/null
@@ -0,0 +1,309 @@
+////////////////////////////////////////////////////////////////////////////////\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
diff --git a/src2/README.txt b/src2/README.txt
new file mode 100644 (file)
index 0000000..c62bb92
--- /dev/null
@@ -0,0 +1,71 @@
+New architecture created on 04/02/09 following a MVC approach
+
+Elements of the architecture:
+
+* Data structure: Attributed tree
+--------------------------------------
+in namespace creaImageIO::tree
+Tree :   Attributed tree structure
+        inherits Node
+        holds a vector of root TreeNode 
+TreeData : Abstract class to store user data on a tree
+Node : 
+        belong to a Tree, 
+        holds a pointer on parent Node, 
+        holds a vector of children Node
+NodeData : Abstract class to store user data on a tree node
+Descriptor : Descriptor of the structure a tree (number of levels, descriptors of each level, ...)
+         
+        holds a vector of LevelDescriptor
+LevelDescriptor : 
+         holds a vector of TreeAttributeDescriptor
+AttributeDescriptor :
+        stores name, dicom group/elem, flags
+Comparator: Abstract definition of a comparator of Node
+           Comparison is done by operator()(Node* const &, Node* const &)
+ComparatorWithOrder : Abstract Comparator whose order can be reversed
+                   Concrete comparison is done by method compare(Node* const &, Node* const &)
+LexicographicalComparator :  A Comparator which stores a vector of Comparators and which performs lexicographical comparison               
+
+IntComparator : Compares the values of a given Attribute of the Nodes which is decoded as an int value
+FloatComparator : Compares the values of a given Attribute of the Nodes which is decoded as a float value
+StringComparator : Compares the values of a given Attribute of the Nodes which is decoded as a string value
+
+
+* Models : TreeHandler and descendants
+--------------------------------------
+
+TreeHandler : Abstract class which 'handles' a Tree structure.
+           Can:
+           Load the children of a given Node
+
+SQLiteTreeHandler : Concrete TreeHandler which manages a tree stored in a sqlite database
+CppSQLite3.h / CppSQLite3.cpp : C++ interface to sqlite db
+...
+
+ImageFinder : Parses (recursively) a part of a filesystem to look for known images and load their attributes in order to add the images to a Tree (submission via a TreeHandler::AddBranch)
+
+
+* Image handling
+----------------
+
+creaImageIOImageReader.h/cpp :
+SpecificImageReader
+ImageReader
+
+creaImageIOMultiThreadImageReader.h/cpp
+creaImageIOIndexedHeap.h/cpp
+
+* Views :
+---------
+TreeView
+WxTreeView
+WxGimmickView
+QTreeView 
+QGimmickView
+...
+
+* Controller :
+--------------
+Gimmick
+...
diff --git a/src2/creaImageIOGimmick.cpp b/src2/creaImageIOGimmick.cpp
new file mode 100644 (file)
index 0000000..a317684
--- /dev/null
@@ -0,0 +1,158 @@
+#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;
+  }
+  //========================================================================
+
+}
diff --git a/src2/creaImageIOGimmick.h b/src2/creaImageIOGimmick.h
new file mode 100644 (file)
index 0000000..7a984a3
--- /dev/null
@@ -0,0 +1,50 @@
+#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  
+
+
diff --git a/src2/creaImageIOImageFinder.cpp b/src2/creaImageIOImageFinder.cpp
new file mode 100644 (file)
index 0000000..8a28144
--- /dev/null
@@ -0,0 +1,288 @@
+#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;
+  }
+  //=====================================================================
+
+
+
+}
diff --git a/src2/creaImageIOImageFinder.h b/src2/creaImageIOImageFinder.h
new file mode 100644 (file)
index 0000000..8eba703
--- /dev/null
@@ -0,0 +1,91 @@
+#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  
+
diff --git a/src2/creaImageIOImageReader.cpp b/src2/creaImageIOImageReader.cpp
new file mode 100644 (file)
index 0000000..2480a47
--- /dev/null
@@ -0,0 +1,505 @@
+#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
diff --git a/src2/creaImageIOImageReader.h b/src2/creaImageIOImageReader.h
new file mode 100644 (file)
index 0000000..e36f226
--- /dev/null
@@ -0,0 +1,70 @@
+#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__
diff --git a/src2/creaImageIOIndexedHeap.h b/src2/creaImageIOIndexedHeap.h
new file mode 100644 (file)
index 0000000..138fc56
--- /dev/null
@@ -0,0 +1,139 @@
+/* 
+
+*/
+/*! \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
diff --git a/src2/creaImageIOMultiThreadImageReader.cpp b/src2/creaImageIOMultiThreadImageReader.cpp
new file mode 100644 (file)
index 0000000..fd76051
--- /dev/null
@@ -0,0 +1,590 @@
+#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
diff --git a/src2/creaImageIOMultiThreadImageReader.h b/src2/creaImageIOMultiThreadImageReader.h
new file mode 100644 (file)
index 0000000..d72981d
--- /dev/null
@@ -0,0 +1,246 @@
+#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__
diff --git a/src2/creaImageIOSQLiteTreeHandler.cpp b/src2/creaImageIOSQLiteTreeHandler.cpp
new file mode 100644 (file)
index 0000000..509aa90
--- /dev/null
@@ -0,0 +1,979 @@
+#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
diff --git a/src2/creaImageIOSQLiteTreeHandler.h b/src2/creaImageIOSQLiteTreeHandler.h
new file mode 100644 (file)
index 0000000..516910e
--- /dev/null
@@ -0,0 +1,190 @@
+#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  
+
diff --git a/src2/creaImageIOSystem.h b/src2/creaImageIOSystem.h
new file mode 100644 (file)
index 0000000..c98f7b8
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __creaImageIOSystem_INCLUDED__
+#define __creaImageIOSystem_INCLUDED__
+
+
+
+#if defined(_WIN32)
+  #ifdef CREAIMAGEIO_EXPORT_SYMBOLS
+    #define CREAIMAGEIO_EXPORT __declspec( dllexport )
+#else
+    #define CREAIMAGEIO_EXPORT __declspec( dllimport )
+  #endif
+  #define CREAIMAGEIO_CDECL __cdecl
+#else
+  #define CREAIMAGEIO_EXPORT
+  #define CREAIMAGEIO_CDECL
+#endif // defined(_WIN32)
+
+#endif
+
diff --git a/src2/creaImageIOTree.cpp b/src2/creaImageIOTree.cpp
new file mode 100644 (file)
index 0000000..f4f1bec
--- /dev/null
@@ -0,0 +1,22 @@
+#include <creaImageIOTree.h>
+
+
+namespace creaImageIO
+{
+  namespace tree
+  {
+    
+    Tree::Tree()
+      : Node(0)
+    {
+      
+    }
+    
+    Tree::~Tree()
+    {
+      
+    }
+
+
+  }
+}
diff --git a/src2/creaImageIOTree.h b/src2/creaImageIOTree.h
new file mode 100644 (file)
index 0000000..854693d
--- /dev/null
@@ -0,0 +1,78 @@
+#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  
diff --git a/src2/creaImageIOTreeAttributeDescriptor.cpp b/src2/creaImageIOTreeAttributeDescriptor.cpp
new file mode 100644 (file)
index 0000000..f01e260
--- /dev/null
@@ -0,0 +1,43 @@
+#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
diff --git a/src2/creaImageIOTreeAttributeDescriptor.h b/src2/creaImageIOTreeAttributeDescriptor.h
new file mode 100644 (file)
index 0000000..cc78838
--- /dev/null
@@ -0,0 +1,77 @@
+#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__
diff --git a/src2/creaImageIOTreeComparators.cpp b/src2/creaImageIOTreeComparators.cpp
new file mode 100644 (file)
index 0000000..7d64496
--- /dev/null
@@ -0,0 +1 @@
+#include <creaImageIOTreeComparators.h>
diff --git a/src2/creaImageIOTreeComparators.h b/src2/creaImageIOTreeComparators.h
new file mode 100644 (file)
index 0000000..d86bd24
--- /dev/null
@@ -0,0 +1,212 @@
+#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__
diff --git a/src2/creaImageIOTreeDescriptor.cpp b/src2/creaImageIOTreeDescriptor.cpp
new file mode 100644 (file)
index 0000000..fc4166f
--- /dev/null
@@ -0,0 +1,152 @@
+#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()]="";
+           }
+       }
+    }
+    //==================================================================
+  }
+}
diff --git a/src2/creaImageIOTreeDescriptor.h b/src2/creaImageIOTreeDescriptor.h
new file mode 100644 (file)
index 0000000..e1812d3
--- /dev/null
@@ -0,0 +1,74 @@
+#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  
diff --git a/src2/creaImageIOTreeHandler.h b/src2/creaImageIOTreeHandler.h
new file mode 100644 (file)
index 0000000..c88bbc9
--- /dev/null
@@ -0,0 +1,114 @@
+#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  
+
diff --git a/src2/creaImageIOTreeLevelDescriptor.cpp b/src2/creaImageIOTreeLevelDescriptor.cpp
new file mode 100644 (file)
index 0000000..7cdd9fd
--- /dev/null
@@ -0,0 +1,10 @@
+#include <creaImageIOTreeLevelDescriptor.h>
+
+namespace creaImageIO
+{
+  namespace tree
+  {
+    
+  }
+}
+
diff --git a/src2/creaImageIOTreeLevelDescriptor.h b/src2/creaImageIOTreeLevelDescriptor.h
new file mode 100644 (file)
index 0000000..95b284b
--- /dev/null
@@ -0,0 +1,52 @@
+#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  
diff --git a/src2/creaImageIOTreeNode.cpp b/src2/creaImageIOTreeNode.cpp
new file mode 100644 (file)
index 0000000..8f03924
--- /dev/null
@@ -0,0 +1,101 @@
+#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;
+  }
+  //=============================================================
+
+
+  }
+
+}
diff --git a/src2/creaImageIOTreeNode.h b/src2/creaImageIOTreeNode.h
new file mode 100644 (file)
index 0000000..07b46cf
--- /dev/null
@@ -0,0 +1,137 @@
+#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__