]> Creatis software - cpPlugins.git/blob - lib/cpPlugins/Interface.cxx
Bug smashed like a boss
[cpPlugins.git] / lib / cpPlugins / Interface.cxx
1 #include <cpPlugins/Interface.h>
2
3 #ifdef cpPlugins_SYS_WINDOWS
4 #  include <Windows.h>
5 #else // cpPlugins_SYS_WINDOWS
6 #  include <dlfcn.h>
7 #endif // cpPlugins_SYS_WINDOWS
8 #include <cpPlugins_dirent.h>
9 #include <cpPlugins/cpPlugins_DynLibs.h>
10 #include <algorithm>
11
12 // -------------------------------------------------------------------------
13 unsigned int cpPlugins::Interface::InterfacesCount = 0;
14
15 // -------------------------------------------------------------------------
16 cpPlugins::Interface::
17 Interface( )
18 {
19   this->UpdatePaths( );
20
21   // Explicitly load all ITK and VTK
22   if( Self::InterfacesCount == 0 )
23   {
24     std::vector< std::string > libs;
25     cpPlugins::TokenizeString( libs, cpPlugins_DynLibs, ";" );
26     this->_AddInstancesLib( libs, cpPlugins_CompilationDir );
27     this->_AddInstancesLib( libs, cpPlugins_InstallationDir );
28
29     for( auto l = libs.begin( ); l != libs.end( ); ++l )
30     {
31       std::string error = "";
32       void* hnd = Self::_DLOpen( *l, error );
33       if( hnd == NULL || error != "" )
34         throw std::runtime_error(
35           std::string( "cpPlugins::Interface: Could not load library \"" ) +
36           *l +
37           std::string( "\": " ) +
38           error
39           );
40
41     } // rof
42
43   } // fi
44   Self::InterfacesCount++;
45 }
46
47 // -------------------------------------------------------------------------
48 cpPlugins::Interface::
49 ~Interface( )
50 {
51   this->UnloadAll( );
52   Self::InterfacesCount--;
53   if( Self::InterfacesCount == 0 )
54   {
55     // TODO: unload vtk and itk
56
57   } // fi
58 }
59
60 // -------------------------------------------------------------------------
61 const cpPlugins::Interface::
62 TFilters& cpPlugins::Interface::
63 GetFilters( )
64 {
65   return( this->m_Filters );
66 }
67
68 // -------------------------------------------------------------------------
69 void cpPlugins::Interface::
70 UpdatePaths( )
71 {
72   // Load environment configuration
73   this->m_Paths.clear( );
74   char* p = std::getenv( cpPlugins_PATHS );
75   if( p != NULL )
76     cpPlugins::TokenizeString( this->m_Paths, p, cpPlugins_SEPARATOR );
77   this->m_Paths.push_back( "." );
78 }
79
80 // -------------------------------------------------------------------------
81 void cpPlugins::Interface::
82 GuessAccesiblePlugins( )
83 {
84   for( auto i = this->m_Paths.begin( ); i != this->m_Paths.end( ); ++i )
85     try { this->LoadPluginDir( *i ); } catch( ... ) { }
86 }
87
88 // -------------------------------------------------------------------------
89 void cpPlugins::Interface::
90 LoadPlugin( const std::string& name )
91 {
92   std::stringstream str;
93   str << cpPlugins_LIB_PREFIX << name << "." << cpPlugins_LIB_EXT;
94   std::string base_name = str.str( );
95   bool found = false;
96   for( auto i = this->m_Paths.begin( ); i != this->m_Paths.end( ); ++i )
97   {
98     std::string filename = *i;
99     if( i->back( ) != '/' )
100       filename += std::string( "/" );
101     filename += base_name;
102     try
103     {
104       this->LoadPluginFile( filename );
105       found = true;
106     }
107     catch( ... )
108     {
109     } // yrt
110
111   } // rof
112   if( !found )
113     throw std::runtime_error(
114       std::string( "cpPlugins::Interface: Plugins library \"" ) +
115       name +
116       std::string( "\" not found." )
117       );
118 }
119
120 // -------------------------------------------------------------------------
121 void cpPlugins::Interface::
122 LoadPluginDir( const std::string& dirname )
123 {
124   DIR* dir;
125   struct dirent* ent;
126   if( ( dir = opendir( dirname.c_str( ) ) ) != NULL )
127   {
128     while( ( ent = readdir( dir ) ) != NULL )
129     {
130       try
131       {
132         this->LoadPluginFile(
133           dirname +
134           std::string( "/" ) +
135           ent->d_name
136           );
137       }
138       catch( ... ) { }
139
140     } // elihw
141     closedir( dir );
142   }
143   else
144     throw std::runtime_error(
145       std::string( "cpPlugins::Interface: Could not load directory " ) +
146       std::string( "\"" ) +  dirname + std::string( "\"" )
147       );
148 }
149
150 // -------------------------------------------------------------------------
151 void cpPlugins::Interface::
152 LoadPluginFile( const std::string& filename )
153 {
154   // Canonical filename
155   auto canonical = cpPlugins::CanonicalPath( filename );
156   if( canonical == "" )
157     throw std::runtime_error(
158       std::string( "cpPlugins::Interface: Library \"" ) +
159       filename +
160       std::string( "\" does not exist." )
161       );
162
163   // Try to load the library
164   std::string error;
165   void* hnd = Self::_DLOpen( canonical, error );
166   if( hnd == NULL || error != "" )
167     throw std::runtime_error(
168       std::string( "cpPlugins::Interface: Could not load library \"" ) +
169       filename +
170       std::string( "\": " ) +
171       error
172       );
173
174   // Get plugin name
175   std::string pl_name = Self::_DLGetName( hnd );
176
177   // Check if it was already loaded
178   if( this->m_DynLibraries.find( pl_name ) != this->m_DynLibraries.end( ) )
179     return;
180
181   // Load filters
182   TFilters filters = Self::_DLGetFilters( hnd );
183
184   // Save the loaded filters info
185   bool save_handler = false;
186   for( auto catIt = filters.begin( ); catIt != filters.end( ); ++catIt )
187   {
188     // Check if the filter is completely new
189     auto act_catIt = this->m_Filters.find( catIt->first );
190     for(
191       auto clsIt = catIt->second.begin( );
192       clsIt != catIt->second.end( );
193       ++clsIt
194       )
195     {
196       bool new_filter = true;
197       if( act_catIt != this->m_Filters.end( ) )
198         new_filter =
199           ( act_catIt->second.find( *clsIt ) == act_catIt->second.end( ) );
200
201       // Ok, it is new
202       if( new_filter )
203       {
204         // Update filters container
205         auto creator = Self::_DLGetCreator( hnd, catIt->first, *clsIt );
206         if( creator != NULL )
207         {
208           this->m_DynFilters[ catIt->first][ *clsIt ] =
209             TDynFunc( pl_name, creator );
210           this->m_Filters[ catIt->first ].insert( *clsIt );
211           save_handler = true;
212
213         } // fi
214
215       } // fi
216
217     } // rof
218
219   } // rof
220
221   // Keep dynlib handler, if needed
222   if( save_handler )
223     this->m_DynLibraries[ pl_name ] = TDynFileInfo( canonical, hnd );
224   else
225     Self::_DLClose( hnd );
226 }
227
228 // -------------------------------------------------------------------------
229 void cpPlugins::Interface::
230 UnloadAll( )
231 {
232   for(
233     auto d = this->m_DynLibraries.begin( );
234     d != this->m_DynLibraries.end( );
235     ++d
236     )
237     Self::_DLClose( d->second.second );
238   this->m_DynLibraries.clear( );
239   this->m_DynFilters.clear( );
240   this->m_Filters.clear( );
241 }
242
243 // -------------------------------------------------------------------------
244 cpPlugins::ProcessObject::Pointer cpPlugins::Interface::
245 Create( const std::string& category, const std::string& name )
246 {
247   typedef cpPlugins::ProcessObject::Pointer _TPointer;
248   _TPointer filter = NULL;
249   auto catIt = this->m_DynFilters.find( category );
250   if( catIt != this->m_DynFilters.end( ) )
251   {
252     auto clsIt = catIt->second.find( name );
253     if( clsIt != catIt->second.end( ) )
254       filter =
255         ( reinterpret_cast< _TPointer* >( clsIt->second.second( ) ) )->
256         GetPointer( );
257
258   } // fi
259   return( filter );
260 }
261
262 // -------------------------------------------------------------------------
263 std::string cpPlugins::Interface::
264 GetPluginName( const std::string& category, const std::string& name ) const
265 {
266   std::string plugin = "";
267   auto catIt = this->m_DynFilters.find( category );
268   if( catIt != this->m_DynFilters.end( ) )
269   {
270     auto clsIt = catIt->second.find( name );
271     if( clsIt != catIt->second.end( ) )
272       plugin = clsIt->second.first;
273
274   } // fi
275   return( plugin );
276 }
277
278 // -------------------------------------------------------------------------
279 std::string cpPlugins::Interface::
280 GetPluginName( const ProcessObject* obj ) const
281 {
282   if( obj != NULL )
283     return(
284       this->GetPluginName(
285         obj->GetClassCategory( ),
286         obj->GetClassName( )
287         )
288       );
289   else
290     return( "" );
291 }
292
293 // -------------------------------------------------------------------------
294 std::set< std::string > cpPlugins::Interface::
295 GetPlugins( ) const
296 {
297   std::set< std::string > res;
298   auto i = this->m_DynLibraries.begin( );
299   for( ; i != this->m_DynLibraries.end( ); ++i )
300     res.insert( i->first );
301   return( res );
302 }
303
304 // -------------------------------------------------------------------------
305 template< class _TList >
306 void cpPlugins::Interface::
307 _AddInstancesLib( _TList& libs, const std::string& path )
308 {
309   DIR* dir;
310   struct dirent* ent;
311   if( ( dir = opendir( path.c_str( ) ) ) != NULL )
312   {
313     while( ( ent = readdir( dir ) ) != NULL )
314     {
315       std::string fname = path + std::string( "/" ) + ent->d_name;
316       if( fname.find( "cpPlugins_Instances_" ) != std::string::npos )
317         libs.push_back( fname );
318
319     } // elihw
320     closedir( dir );
321
322   } // fi
323 }
324
325 // -------------------------------------------------------------------------
326 void* cpPlugins::Interface::
327 _DLOpen( const std::string& fname, std::string& error )
328 {
329   void* hnd = NULL;
330 #ifdef cpPlugins_SYS_WINDOWS
331   hnd = ::LoadLibraryA( fname.c_str( ) );
332 #else // cpPlugins_SYS_WINDOWS
333   hnd = dlopen( fname.c_str( ), RTLD_NOW | RTLD_GLOBAL );
334   if( hnd == NULL )
335     error = dlerror( );
336   else
337     dlerror( );
338 #endif // cpPlugins_SYS_WINDOWS
339   return( hnd );
340 }
341
342 // -------------------------------------------------------------------------
343 const char* cpPlugins::Interface::
344 _DLGetName( void* hnd )
345 {
346   // Get descriptors
347   typedef const char* ( *f_t )( );
348   f_t f = NULL;
349 #ifdef cpPlugins_SYS_WINDOWS
350   f = ( f_t )( ::GetProcAddress( ( HMODULE )hnd, "cpPlugins_Name" ) );
351 #else // cpPlugins_SYS_WINDOWS
352   f = ( f_t )( dlsym( hnd, "cpPlugins_Name" ) );
353 #endif // cpPlugins_SYS_WINDOWS
354   if( f == NULL )
355   {
356     Self::_DLClose( hnd );
357     throw std::runtime_error(
358       "cpPlugins::Interface: Library not recognized as a cpPlugins library."
359       );
360
361   } // fi
362   return( f( ) );
363 }
364
365 // -------------------------------------------------------------------------
366 cpPlugins::Interface::
367 TFilters cpPlugins::Interface::
368 _DLGetFilters( void* hnd )
369 {
370   // Get descriptors
371   typedef const char* ( *f_t )( );
372   f_t f = NULL;
373 #ifdef cpPlugins_SYS_WINDOWS
374   f = ( f_t )( ::GetProcAddress( ( HMODULE )hnd, "cpPlugins_LoadedFilters" ) );
375 #else // cpPlugins_SYS_WINDOWS
376   f = ( f_t )( dlsym( hnd, "cpPlugins_LoadedFilters" ) );
377 #endif // cpPlugins_SYS_WINDOWS
378   if( f == NULL )
379   {
380     Self::_DLClose( hnd );
381     throw std::runtime_error(
382       "cpPlugins::Interface: Library not recognized as a cpPlugins library."
383       );
384
385   } // fi
386   std::string descriptors = f( );
387
388   // Demangle descriptors
389   TFilters filters;
390   std::replace( descriptors.begin( ), descriptors.end( ), ';', ' ' );
391   std::istringstream str( descriptors );
392   while( str )
393   {
394     std::string value, category, name;
395     str >> value;
396     if( value == "" )
397       continue;
398     std::replace( value.begin( ), value.end( ), ':', ' ' );
399     std::istringstream value_str( value );
400     value_str >> category >> name;
401     filters[ category ].insert( name );
402
403   } // elihw
404   return( filters );
405 }
406
407 // -------------------------------------------------------------------------
408 cpPlugins::Interface::
409 TCreator cpPlugins::Interface::
410 _DLGetCreator(
411   void* hnd, const std::string& category, const std::string& name
412   )
413 {
414   TCreator c = NULL;
415   std::string func_name = category + "_" + name;
416 #ifdef cpPlugins_SYS_WINDOWS
417   c = ( TCreator )( ::GetProcAddress( ( HMODULE )hnd, func_name.c_str( ) ) );
418 #else // cpPlugins_SYS_WINDOWS
419   c = ( TCreator )( dlsym( hnd, func_name.c_str( ) ) );
420 #endif // cpPlugins_SYS_WINDOWS
421   if( c == NULL )
422     throw std::runtime_error(
423       std::string( "cpPlugins::Interface: Class \"" ) +
424       category + std::string( ":" ) + name +
425       std::string( "\" does not have a valid creator function." )
426       );
427   return( c );
428 }
429
430 // -------------------------------------------------------------------------
431 void cpPlugins::Interface::
432 _DLClose( void* hnd )
433 {
434 #ifdef cpPlugins_SYS_WINDOWS
435   ::FreeLibrary( ( HMODULE )hnd );
436 #else // cpPlugins_SYS_WINDOWS
437   dlclose( hnd );
438 #endif // cpPlugins_SYS_WINDOWS
439 }
440
441 // eof - $RCSfile$