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