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