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