Salome HOME
594ed26892c219c9dc39990ef44b81e640b19da4
[modules/gui.git] / src / SalomeApp / SalomeApp_Engine_i.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //  SalomeApp_Engine_i : implementation of SalomeApp_Engine.idl
24 //  File   : SalomeApp_Engine_i.cxx
25 //  Author : Alexander SLADKOV
26
27 #include "SalomeApp_Engine_i.h"
28 #include "SalomeApp_Application.h"
29 #include "SalomeApp_Study.h"
30 #include "SUIT_Session.h"
31 #include "CAM_Module.h"
32 #include "LightApp_DataModel.h"
33
34 #include <SALOME_NamingService.hxx>
35 #include <SALOMEDS_Tool.hxx>
36 #include <Utils_ORB_INIT.hxx>
37 #include <Utils_SINGLETON.hxx>
38 #include <Utils_SALOME_Exception.hxx>
39 #include <utilities.h>
40
41 #include <QApplication>
42 #include <QDir>
43 #include <QFile>
44
45 #include <iostream>
46
47 /*!
48   Constructor
49 */
50 SalomeApp_Engine_i::SalomeApp_Engine_i( const char* theComponentName )
51   : myComponentName( theComponentName )
52 {
53   MESSAGE("SalomeApp_Engine_i::SalomeApp_Engine_i(): myComponentName = " <<
54           qPrintable( myComponentName ) << ", this = " << this);
55 }
56
57 /*!
58   Destructor
59 */
60 SalomeApp_Engine_i::~SalomeApp_Engine_i()
61 {
62   MESSAGE("SalomeApp_Engine_i::~SalomeApp_Engine_i(): myComponentName = " << 
63           qPrintable( myComponentName ) << ", this = " << this);
64 }
65
66 SALOMEDS::TMPFile* SalomeApp_Engine_i::Save (SALOMEDS::SComponent_ptr theComponent,
67                                              const char* theURL,
68                                              bool isMultiFile)
69 {
70   SALOMEDS::TMPFile_var aStreamFile = new SALOMEDS::TMPFile;
71
72
73   if (CORBA::is_nil(theComponent))
74     return aStreamFile._retn();
75   
76   // Component type
77   QString componentName (theComponent->ComponentDataType());
78
79   // Error somewhere outside - Save() called with wrong SComponent instance
80   if ( myComponentName != componentName )
81     return aStreamFile._retn();
82     
83   // Get a temporary directory to store a file
84   //std::string aTmpDir = isMultiFile ? theURL : SALOMEDS_Tool::GetTmpDir();
85
86   bool manuallySaved = false;
87
88   if ( GetListOfFiles().empty() ) {
89
90     // Save was probably called from outside GUI, so SetListOfFiles was not called!
91     // Try to get list of files from directly from data model
92
93     MESSAGE("SalomeApp_Engine_i::Save(): myComponentName = " <<
94             qPrintable( myComponentName ) <<
95             "it seems Save() was called from outside GUI" );
96
97     // - Get app rnv
98     SalomeApp_Application* app = 
99       dynamic_cast<SalomeApp_Application*>(SUIT_Session::session()->activeApplication());
100     if ( !app )
101       return aStreamFile._retn();
102
103     // - Get study
104     SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
105
106     if ( !study )
107       return aStreamFile._retn();
108     QString url = QString::fromStdString(study->studyDS()->URL());
109
110     // - Get module
111     CAM_Module* module = app->module( SalomeApp_Application::moduleTitle( componentName ) );
112     if ( !module ) // load module???
113       return aStreamFile._retn();
114     // - Get data model
115     LightApp_DataModel* dataModel = dynamic_cast<LightApp_DataModel*>( module->dataModel() );
116     if ( !dataModel )
117       return aStreamFile._retn();
118     // - Save data files
119     QStringList dataFiles;
120     // we use 'url' instead of 'theURL' as latter normally contains path to the tmp dir,
121     // but not actual study's URL
122     dataModel->saveAs( url, study, dataFiles );
123     std::vector<std::string> names;
124     foreach ( QString name, dataFiles ) {
125       if ( !name.isEmpty() )
126         names.push_back(name.toUtf8().data());
127     }
128     SetListOfFiles( names );
129     manuallySaved = true;
130   }
131
132   // Get a temporary directory to store a file
133   //std::string aTmpDir = isMultiFile ? theURL : SALOMEDS_Tool::GetTmpDir();
134
135   // listOfFiles must contain temporary directory name in its first item
136   // and names of files (relatively the temporary directory) in the others
137   const int n = myListOfFiles.size() - 1;
138   
139   if (n > 0) { // there are some files, containing persistent data of the component
140     std::string aTmpDir = myListOfFiles[0];
141     
142     // Create a list to store names of created files
143     ListOfFiles aSeq;
144     aSeq.reserve(n);
145     for (int i = 0; i < n; i++)
146       aSeq.push_back(CORBA::string_dup(myListOfFiles[i + 1].c_str()));
147     
148     // Convert a file to the byte stream
149     aStreamFile = SALOMEDS_Tool::PutFilesToStream(aTmpDir.c_str(), aSeq, isMultiFile);
150     
151     // Remove the files and tmp directory, created by the component storage procedure
152     if (!isMultiFile) SALOMEDS_Tool::RemoveTemporaryFiles(aTmpDir.c_str(), aSeq, true);
153   }
154   
155   if ( manuallySaved )
156     SetListOfFiles( ListOfFiles());
157
158   return aStreamFile._retn();
159 }
160
161 CORBA::Boolean SalomeApp_Engine_i::Load (SALOMEDS::SComponent_ptr theComponent,
162                                          const SALOMEDS::TMPFile& theFile,
163                                          const char* theURL,
164                                          bool isMultiFile)
165 {
166   std::cout << "SalomeApp_Engine_i::Load() isMultiFile = " << isMultiFile << std::endl;
167   if (CORBA::is_nil(theComponent))
168     return false;
169
170   // Error somewhere outside - Load() called with
171   // wrong SComponent instance
172   QString componentName = theComponent->ComponentDataType();
173   if ( myComponentName != componentName )
174     return false;
175
176   // Create a temporary directory for the component's data files
177   std::string aTmpDir = isMultiFile ? theURL : SALOMEDS_Tool::GetTmpDir();
178
179   // Convert the byte stream theStream to a files and place them in the tmp directory.
180   // The files and temporary directory must be deleted by the component loading procedure.
181   ListOfFiles aSeq =
182     SALOMEDS_Tool::PutStreamToFiles(theFile, aTmpDir.c_str(), isMultiFile);
183
184   // Store list of file names to be used by the component loading procedure
185   const int n = aSeq.size() + 1;
186   ListOfFiles listOfFiles (n);
187   listOfFiles[0] = aTmpDir;
188   for (int i = 1; i < n; i++)
189     listOfFiles[i] = std::string(aSeq[i - 1]);
190
191   SetListOfFiles(listOfFiles);
192
193   return true;
194 }
195
196 SalomeApp_Engine_i::ListOfFiles SalomeApp_Engine_i::GetListOfFiles()
197 {
198   return myListOfFiles;
199 }
200
201 void SalomeApp_Engine_i::SetListOfFiles (const ListOfFiles& theListOfFiles)
202 {
203   myListOfFiles = theListOfFiles;
204 }
205
206 /*! 
207  *  DumpPython implementation for light modules
208  */
209 Engines::TMPFile* SalomeApp_Engine_i::DumpPython(CORBA::Boolean isPublished,
210                                                                          CORBA::Boolean isMultiFile,
211                                                                          CORBA::Boolean& isValidScript)
212 {
213   MESSAGE("SalomeApp_Engine_i::DumpPython(): myComponentName = "<<
214           qPrintable( myComponentName ) << ", this = " << this);
215   
216   // Temporary solution: returning a non-empty sequence
217   // even if there's nothing to dump, to avoid crashes in SALOMEDS
218   // TODO: Improve SALOMEDSImpl_Study::DumpStudy() by skipping the components 
219   // with isValidScript == false, and initialize isValidScript by false below.
220   Engines::TMPFile_var aStreamFile = new Engines::TMPFile(1);
221   aStreamFile->length( 1 );
222   aStreamFile[0] = '\0';
223   isValidScript = true;
224
225   // listOfFiles must contain temporary directory name in its first item
226   // and names of files (relatively the temporary directory) in the others
227   if ( myListOfFiles.size() < 2 )
228     return aStreamFile._retn();
229
230   // there are some files, containing persistent data of the component
231   QString aTmpPath( myListOfFiles.front().c_str() );
232   QDir aTmpDir( aTmpPath );
233   if ( !aTmpDir.exists() )
234     return aStreamFile._retn();    
235
236   // Calculate file sizes
237   QStringList aFilePaths;
238   QList<qint64> aFileSizes;
239   qint64 aBuffSize = 0;
240   ListOfFiles::const_iterator aFIt  = myListOfFiles.begin();
241   ListOfFiles::const_iterator aFEnd = myListOfFiles.end();
242   aFIt++;
243   for (; aFIt != aFEnd; aFIt++){
244     QString aFileName( (*aFIt).c_str() );
245     if ( !aTmpDir.exists( aFileName ) ){
246       continue;
247     }
248
249     QFile aFile( aTmpDir.filePath( aFileName ) );
250     if ( !aFile.open( QIODevice::ReadOnly ) ){
251       continue;
252     }
253
254     aFilePaths.push_back( aTmpDir.filePath( aFileName ) );
255     aFileSizes.push_back( aFile.size() );
256     aBuffSize += aFileSizes.back();
257
258     aFile.close();
259   }
260
261   if ( !aFilePaths.size() || !aBuffSize )
262     return aStreamFile._retn(); 
263     
264   char* aBuffer = new char[aBuffSize + 1];
265   if ( !aBuffer )
266     return aStreamFile._retn();
267
268   // Convert the file(s) to the byte stream, multiple files are simply
269   // concatenated
270   // TODO: imporve multi-script support if necessary...
271   qint64 aCurrPos = 0;
272   QStringList::const_iterator aFileIt  = aFilePaths.begin();
273   QStringList::const_iterator aFileEnd = aFilePaths.end();
274   QList<qint64>::const_iterator   aSIt = aFileSizes.begin();
275   for ( ; aFileIt != aFileEnd; aFileIt++, aSIt++ ){
276     QFile aFile( aTmpDir.filePath( *aFileIt ) );
277     if ( !aFile.open( QIODevice::ReadOnly ) ){
278       continue;
279     }
280
281     // Incorrect size of file
282     // Do not remove the bad file to have some diagnostic means
283     if ( aFile.read( aBuffer + aCurrPos, *aSIt ) != *aSIt ){
284       aFile.close();      
285       return aStreamFile._retn();
286     }
287
288     aCurrPos += (*aSIt); 
289     aFile.remove();   
290   }
291
292   // Here we should end up with empty aTmpDir
293   // TODO: Handle QDir::rmdir() error status somehow...
294   aTmpDir.rmdir( aTmpPath );
295
296   aBuffer[aBuffSize] = '\0';
297   CORBA::Octet* anOctetBuf =  (CORBA::Octet*)aBuffer;
298   aStreamFile = new Engines::TMPFile(aBuffSize + 1, aBuffSize + 1, anOctetBuf, 1); 
299
300   return aStreamFile._retn();
301 }
302
303 /*!
304   \return Component data type string for this instance of the engine
305 */
306 char* SalomeApp_Engine_i::ComponentDataType()
307 {
308   return CORBA::string_dup( myComponentName.toLatin1().constData() );
309 }
310
311 /*!
312   \return Component version
313 */
314 char* SalomeApp_Engine_i::getVersion()
315 {
316   SalomeApp_Application::ModuleShortInfoList versions = SalomeApp_Application::getVersionInfo();
317   QString version;
318   SalomeApp_Application::ModuleShortInfo version_info;
319   foreach ( version_info, versions ) {
320     if ( SalomeApp_Application::moduleName( version_info.name ) == myComponentName ) {
321       version = version_info.version;
322       break;
323     }
324   }
325   
326   return CORBA::string_dup( version.toLatin1().constData() );
327 }
328
329 /*!
330   \return 
331 */
332 CORBA::ORB_var SalomeApp_Engine_i::orb()
333 {
334   static CORBA::ORB_var _orb;
335
336   if ( CORBA::is_nil( _orb ) ) {
337     Qtx::CmdLineArgs args;
338     ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
339     _orb = init( args.argc(), args.argv() );
340   }
341
342   return _orb;
343 }
344
345 /*!
346   \return 
347 */
348 PortableServer::POA_var SalomeApp_Engine_i::poa()
349 {
350   static PortableServer::POA_var _poa;
351   if ( CORBA::is_nil( _poa ) ){
352     CORBA::Object_var obj = orb()->resolve_initial_references( "RootPOA" );
353     _poa = PortableServer::POA::_narrow( obj );
354   }
355   return _poa;
356 }
357
358 /*!
359   \return 
360 */
361 SALOME_NamingService* SalomeApp_Engine_i::namingService()
362 {
363   static SALOME_NamingService _ns(orb());
364   return &_ns;
365 }
366
367 /*!
368   Internal method, creates a CORBA engine for a light SALOME module
369   with the given "component data type" string,
370   activates it and registers in SALOME naming service with
371   /SalomeAppEngine/comp_data_type path. If the engine is already in the 
372   naming service, simply returns and object reference to it.
373   \param theComponentName - synthetic "component data type" used to identify a given light module
374   \return Object reference to the CORBA engine
375 */
376 CORBA::Object_ptr SalomeApp_Engine_i::EngineForComponent( const char* theComponentName,
377                                                           bool toCreate )
378 {
379   CORBA::Object_var anEngine;
380   if ( !theComponentName || !strlen( theComponentName ) )
381     return anEngine._retn();
382
383   if ( SalomeApp_Application::moduleTitle( theComponentName ).isEmpty() )
384     return anEngine._retn();
385
386   std::string aPath( "/SalomeAppEngine/" );
387   aPath += theComponentName;
388   anEngine = namingService()->Resolve( aPath.c_str() );
389
390   // Activating a new instance of the servant
391   if ( toCreate && CORBA::is_nil( anEngine ) ){
392     try {
393       SalomeApp_Engine_i* aServant    = new SalomeApp_Engine_i( theComponentName );
394       PortableServer::ObjectId_var id = poa()->activate_object( aServant );
395       anEngine = aServant->_this();
396       aServant->_remove_ref();
397       namingService()->Register( anEngine.in(), aPath.c_str() );
398     }
399     catch (CORBA::SystemException&) {
400       INFOS("Caught CORBA::SystemException.");
401     }
402     catch (CORBA::Exception&) {
403       INFOS("Caught CORBA::Exception.");
404     }
405     catch (...) {
406       INFOS("Caught unknown exception.");
407     }
408   }
409
410   return anEngine._retn();
411 }
412
413 /*!
414   \param theComponentName - synthetic "component data type" used to identify a given light module
415   \return IOR string for the CORBA engine for a light SALOME module
416   with the given "component data type" string
417   \sa GetInstance( const char* theComponentName )
418 */
419 std::string SalomeApp_Engine_i::EngineIORForComponent( const char* theComponentName,
420                                                        bool toCreate )
421 {
422   std::string anIOR( "" );
423   CORBA::Object_var anEngine = EngineForComponent( theComponentName, toCreate );
424   if ( !CORBA::is_nil( anEngine ) )
425   {
426     CORBA::String_var objStr = orb()->object_to_string( anEngine.in() );
427     anIOR = std::string( objStr.in() );
428   }
429   return anIOR;
430 }
431
432 /*!
433   \param theComponentName - synthetic "component data type" used to identify a given light module
434   \return A pointer to corresponding C++ engine instance, null means some internal problems.
435   \sa EngineIORForComponent( const char* theComponentName )
436 */
437 SalomeApp_Engine_i* SalomeApp_Engine_i::GetInstance( const char* theComponentName,
438                                                      bool toCreate )
439 {
440   SalomeApp_Engine_i* aServant = 0;
441   CORBA::Object_var anEngine = EngineForComponent( theComponentName, toCreate );
442   if ( !CORBA::is_nil( anEngine ) )
443   {
444     PortableServer::Servant aServantBase = poa()->reference_to_servant( anEngine.in() );
445     aServant = dynamic_cast<SalomeApp_Engine_i*>( aServantBase );
446   } 
447   MESSAGE("SalomeApp_Engine_i::GetInstance(): theComponentName = " <<
448           theComponentName << ", aServant = " << aServant);
449   return aServant;
450 }