]> Creatis software - cpPlugins.git/blob - lib/cpPlugins/Interface.cxx
8b5e106e5791c29d697ab123f414c56359248818
[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::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::string cpPlugins::Interface::
243 GetPluginName( const ProcessObject* obj ) const
244 {
245   if( obj != NULL )
246     return(
247       this->GetPluginName(
248         obj->GetClassCategory( ),
249         obj->GetClassName( )
250         )
251       );
252   else
253     return( "" );
254 }
255
256 // -------------------------------------------------------------------------
257 std::set< std::string > cpPlugins::Interface::
258 GetPlugins( ) const
259 {
260   std::set< std::string > res;
261   auto i = this->m_DynLibraries.begin( );
262   for( ; i != this->m_DynLibraries.end( ); ++i )
263     res.insert( i->first );
264   return( res );
265 }
266
267 // -------------------------------------------------------------------------
268 void* cpPlugins::Interface::
269 _DLOpen( const std::string& fname )
270 {
271   void* hnd = NULL;
272 #ifdef cpPlugins_SYS_WINDOWS
273   hnd = ::LoadLibraryA( fname.c_str( ) );
274 #else // cpPlugins_SYS_WINDOWS
275   hnd = dlopen( fname.c_str( ), RTLD_NOW | RTLD_GLOBAL );
276   dlerror( );
277 #endif // cpPlugins_SYS_WINDOWS
278   return( hnd );
279 }
280
281 // -------------------------------------------------------------------------
282 const char* cpPlugins::Interface::
283 _DLGetName( void* hnd )
284 {
285   // Get descriptors
286   typedef const char* ( *f_t )( );
287   f_t f = NULL;
288 #ifdef cpPlugins_SYS_WINDOWS
289   f = ( f_t )( ::GetProcAddress( ( HMODULE )hnd, "cpPlugins_Name" ) );
290 #else // cpPlugins_SYS_WINDOWS
291   f = ( f_t )( dlsym( hnd, "cpPlugins_Name" ) );
292 #endif // cpPlugins_SYS_WINDOWS
293   if( f == NULL )
294   {
295     Self::_DLClose( hnd );
296     throw std::runtime_error(
297       "cpPlugins::Interface: Library not recognized as a cpPlugins library."
298       );
299
300   } // fi
301   return( f( ) );
302 }
303
304 // -------------------------------------------------------------------------
305 cpPlugins::Interface::
306 TFilters cpPlugins::Interface::
307 _DLGetFilters( void* hnd )
308 {
309   // Get descriptors
310   typedef const char* ( *f_t )( );
311   f_t f = NULL;
312 #ifdef cpPlugins_SYS_WINDOWS
313   f = ( f_t )( ::GetProcAddress( ( HMODULE )hnd, "cpPlugins_LoadedFilters" ) );
314 #else // cpPlugins_SYS_WINDOWS
315   f = ( f_t )( dlsym( hnd, "cpPlugins_LoadedFilters" ) );
316 #endif // cpPlugins_SYS_WINDOWS
317   if( f == NULL )
318   {
319     Self::_DLClose( hnd );
320     throw std::runtime_error(
321       "cpPlugins::Interface: Library not recognized as a cpPlugins library."
322       );
323
324   } // fi
325   std::string descriptors = f( );
326
327   // Demangle descriptors
328   TFilters filters;
329   std::replace( descriptors.begin( ), descriptors.end( ), ';', ' ' );
330   std::istringstream str( descriptors );
331   while( str )
332   {
333     std::string value, category, name;
334     str >> value;
335     if( value == "" )
336       continue;
337     std::replace( value.begin( ), value.end( ), ':', ' ' );
338     std::istringstream value_str( value );
339     value_str >> category >> name;
340     filters[ category ].insert( name );
341
342   } // elihw
343   return( filters );
344 }
345
346 // -------------------------------------------------------------------------
347 cpPlugins::Interface::
348 TCreator cpPlugins::Interface::
349 _DLGetCreator(
350   void* hnd, const std::string& category, const std::string& name
351   )
352 {
353   TCreator c = NULL;
354   std::string func_name = category + "_" + name;
355 #ifdef cpPlugins_SYS_WINDOWS
356   c = ( TCreator )( ::GetProcAddress( ( HMODULE )hnd, func_name.c_str( ) ) );
357 #else // cpPlugins_SYS_WINDOWS
358   c = ( TCreator )( dlsym( hnd, func_name.c_str( ) ) );
359 #endif // cpPlugins_SYS_WINDOWS
360   if( c == NULL )
361     throw std::runtime_error(
362       std::string( "cpPlugins::Interface: Class \"" ) +
363       category + std::string( ":" ) + name +
364       std::string( "\" does not have a valid creator function." )
365       );
366   return( c );
367 }
368
369 // -------------------------------------------------------------------------
370 void cpPlugins::Interface::
371 _DLClose( void* hnd )
372 {
373 #ifdef cpPlugins_SYS_WINDOWS
374   ::FreeLibrary( ( HMODULE )hnd );
375 #else // cpPlugins_SYS_WINDOWS
376   dlclose( hnd );
377 #endif // cpPlugins_SYS_WINDOWS
378 }
379
380 // eof - $RCSfile$