Salome HOME
Merge ASERIS development.
[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 namespace
48 {
49   SalomeApp_Study* getStudyById( int id )
50   {
51     SalomeApp_Study* study = 0;
52     QList<SUIT_Application*> apps = SUIT_Session::session()->applications();
53     for ( int i = 0; i < apps.count() && !study; i++ )
54     {
55       SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( apps[i]->activeStudy() );
56       if ( appStudy && appStudy->id() == id )
57         study = appStudy;
58     }
59     return study;
60   }
61 }
62
63 /*!
64   Constructor
65 */
66 SalomeApp_Engine_i::SalomeApp_Engine_i( const char* theComponentName )
67   : myComponentName( theComponentName )
68 {
69   MESSAGE("SalomeApp_Engine_i::SalomeApp_Engine_i(): myComponentName = " <<
70           qPrintable( myComponentName ) << ", this = " << this);
71 }
72
73 /*!
74   Destructor
75 */
76 SalomeApp_Engine_i::~SalomeApp_Engine_i()
77 {
78   MESSAGE("SalomeApp_Engine_i::~SalomeApp_Engine_i(): myComponentName = " << 
79           qPrintable( myComponentName ) << ", this = " << this);
80 }
81
82 SALOMEDS::TMPFile* SalomeApp_Engine_i::Save (SALOMEDS::SComponent_ptr theComponent,
83                                              const char* theURL,
84                                              bool isMultiFile)
85 {
86   SALOMEDS::TMPFile_var aStreamFile = new SALOMEDS::TMPFile;
87
88   if ( CORBA::is_nil(theComponent) || CORBA::is_nil( theComponent->GetStudy() ) )
89     return aStreamFile._retn();
90
91   // Component type
92   QString componentName = theComponent->ComponentDataType();
93   // Error somewhere outside - Save() called with wrong SComponent instance
94   if ( myComponentName != componentName )
95     return aStreamFile._retn();
96
97   // Get study ID
98   const int studyId = theComponent->GetStudy()->StudyId();
99
100   bool manuallySaved = false;
101
102   if ( !myMap.count( studyId ) ) {
103     // Save was probably called from outside GUI, so SetListOfFiles was not called!
104     // Try to get list of files from directly from data model
105
106     MESSAGE("SalomeApp_Engine_i::Save(): myComponentName = " <<
107             qPrintable( myComponentName ) <<
108             "it seems Save() was called from outside GUI" );
109
110     // - Get study
111     SalomeApp_Study* study = getStudyById( studyId );
112     if ( !study )
113       return aStreamFile._retn();
114     QString url = QString::fromStdString(study->studyDS()->URL());
115     // - Get app
116     SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( study->application() );
117     if ( !app )
118       return aStreamFile._retn();
119     // - Get module
120     CAM_Module* module = app->module( SalomeApp_Application::moduleTitle( componentName ) );
121     if ( !module ) // load module???
122       return aStreamFile._retn();
123     // - Get data model
124     LightApp_DataModel* dataModel = dynamic_cast<LightApp_DataModel*>( module->dataModel() );
125     if ( !dataModel )
126       return aStreamFile._retn();
127     // - Save data files
128     QStringList dataFiles;
129     // we use 'url' instead of 'theURL' as latter normally contains path to the tmp dir,
130     // but not actual study's URL
131     dataModel->saveAs( url, study, dataFiles );
132     std::vector<std::string> names;
133     foreach ( QString name, dataFiles ) {
134       if ( !name.isEmpty() )
135         names.push_back(name.toUtf8().data());
136     }
137     SetListOfFiles( names, studyId );
138     manuallySaved = true;
139   }
140
141   // Get a temporary directory to store a file
142   //std::string aTmpDir = isMultiFile ? theURL : SALOMEDS_Tool::GetTmpDir();
143
144   if ( myMap.count( studyId ) ) {
145
146     const ListOfFiles& listOfFiles = myMap[studyId];
147
148     // listOfFiles must contain temporary directory name in its first item
149     // and names of files (relatively the temporary directory) in the others
150     const int n = listOfFiles.size() - 1;
151
152     if (n > 0) { // there are some files, containing persistent data of the component
153       std::string aTmpDir = listOfFiles[0];
154
155       // Create a list to store names of created files
156       SALOMEDS::ListOfFileNames_var aSeq = new SALOMEDS::ListOfFileNames;
157       aSeq->length(n);
158       for (int i = 0; i < n; i++)
159         aSeq[i] = CORBA::string_dup(listOfFiles[i + 1].c_str());
160
161       // Convert a file to the byte stream
162       aStreamFile = SALOMEDS_Tool::PutFilesToStream(aTmpDir.c_str(), aSeq.in(), isMultiFile);
163
164       // Remove the files and tmp directory, created by the component storage procedure
165       if (!isMultiFile) SALOMEDS_Tool::RemoveTemporaryFiles(aTmpDir.c_str(), aSeq.in(), true);
166     }
167   }
168
169   if ( manuallySaved )
170     SetListOfFiles( ListOfFiles(), studyId );
171
172   return aStreamFile._retn();
173 }
174
175 CORBA::Boolean SalomeApp_Engine_i::Load (SALOMEDS::SComponent_ptr theComponent,
176                                          const SALOMEDS::TMPFile& theFile,
177                                          const char* theURL,
178                                          bool isMultiFile)
179 {
180   std::cout << "SalomeApp_Engine_i::Load() isMultiFile = " << isMultiFile << std::endl;
181   if (CORBA::is_nil(theComponent) || CORBA::is_nil(theComponent->GetStudy()))
182     return false;
183
184   // Error somewhere outside - Load() called with
185   // wrong SComponent instance
186   QString componentName = theComponent->ComponentDataType();
187   if ( myComponentName != componentName )
188     return false;
189
190   const int studyId = theComponent->GetStudy()->StudyId();
191
192   // Create a temporary directory for the component's data files
193   std::string aTmpDir = isMultiFile ? theURL : SALOMEDS_Tool::GetTmpDir();
194
195   // Convert the byte stream theStream to a files and place them in the tmp directory.
196   // The files and temporary directory must be deleted by the component loading procedure.
197   SALOMEDS::ListOfFileNames_var aSeq =
198     SALOMEDS_Tool::PutStreamToFiles(theFile, aTmpDir.c_str(), isMultiFile);
199
200   // Store list of file names to be used by the component loading procedure
201   const int n = aSeq->length() + 1;
202   ListOfFiles listOfFiles (n);
203   listOfFiles[0] = aTmpDir;
204   for (int i = 1; i < n; i++)
205     listOfFiles[i] = std::string(aSeq[i - 1]);
206
207   SetListOfFiles(listOfFiles, studyId);
208
209   return true;
210 }
211
212 SalomeApp_Engine_i::ListOfFiles SalomeApp_Engine_i::GetListOfFiles (const int theStudyId)
213 {
214   ListOfFiles aListOfFiles;
215
216   if (myMap.find(theStudyId) != myMap.end())
217   {
218     aListOfFiles = myMap[theStudyId];
219   }
220
221   return aListOfFiles;
222 }
223
224 void SalomeApp_Engine_i::SetListOfFiles (const ListOfFiles& theListOfFiles,
225                                          const int          theStudyId)
226 {
227   if ( theListOfFiles.empty() )
228     myMap.erase( theStudyId );
229   else
230     myMap[theStudyId] = theListOfFiles;
231 }
232
233 /*! 
234  *  DumpPython implementation for light modules
235  */
236 Engines::TMPFile* SalomeApp_Engine_i::DumpPython(CORBA::Object_ptr theStudy, 
237                                                  CORBA::Boolean isPublished, 
238                                                  CORBA::Boolean isMultiFile, 
239                                                  CORBA::Boolean& isValidScript)
240 {
241   MESSAGE("SalomeApp_Engine_i::DumpPython(): myComponentName = "<<
242           qPrintable( myComponentName ) << ", this = " << this);
243   
244   // Temporary solution: returning a non-empty sequence
245   // even if there's nothing to dump, to avoid crashes in SALOMEDS
246   // TODO: Improve SALOMEDSImpl_Study::DumpStudy() by skipping the components 
247   // with isValidScript == false, and initialize isValidScript by false below.
248   Engines::TMPFile_var aStreamFile = new Engines::TMPFile(1);
249   aStreamFile->length( 1 );
250   aStreamFile[0] = '\0';
251   isValidScript = true;
252
253   if (CORBA::is_nil(theStudy))
254     return aStreamFile._retn();
255
256   SALOMEDS::Study_var studyDS = SALOMEDS::Study::_narrow( theStudy );
257   const int studyId = studyDS->StudyId();
258
259   if (!myMap.count(studyId))
260     return aStreamFile._retn();
261
262   ListOfFiles listOfFiles = myMap[studyId];
263
264   // listOfFiles must contain temporary directory name in its first item
265   // and names of files (relatively the temporary directory) in the others
266   if ( listOfFiles.size() < 2 ) 
267     return aStreamFile._retn();
268
269   // there are some files, containing persistent data of the component
270   QString aTmpPath( listOfFiles.front().c_str() );
271   QDir aTmpDir( aTmpPath );
272   if ( !aTmpDir.exists() )
273     return aStreamFile._retn();    
274
275   // Calculate file sizes
276   QStringList aFilePaths;
277   QList<qint64> aFileSizes;
278   qint64 aBuffSize = 0;
279   ListOfFiles::const_iterator aFIt  = listOfFiles.begin();
280   ListOfFiles::const_iterator aFEnd = listOfFiles.end();
281   aFIt++;
282   for (; aFIt != aFEnd; aFIt++){
283     QString aFileName( (*aFIt).c_str() );
284     if ( !aTmpDir.exists( aFileName ) ){
285       continue;
286     }
287
288     QFile aFile( aTmpDir.filePath( aFileName ) );
289     if ( !aFile.open( QIODevice::ReadOnly ) ){
290       continue;
291     }
292
293     aFilePaths.push_back( aTmpDir.filePath( aFileName ) );
294     aFileSizes.push_back( aFile.size() );
295     aBuffSize += aFileSizes.back();
296
297     aFile.close();
298   }
299
300   if ( !aFilePaths.size() || !aBuffSize )
301     return aStreamFile._retn(); 
302     
303   char* aBuffer = new char[aBuffSize + 1];
304   if ( !aBuffer )
305     return aStreamFile._retn();
306
307   // Convert the file(s) to the byte stream, multiple files are simply
308   // concatenated
309   // TODO: imporve multi-script support if necessary...
310   qint64 aCurrPos = 0;
311   QStringList::const_iterator aFileIt  = aFilePaths.begin();
312   QStringList::const_iterator aFileEnd = aFilePaths.end();
313   QList<qint64>::const_iterator   aSIt = aFileSizes.begin();
314   for ( ; aFileIt != aFileEnd; aFileIt++, aSIt++ ){
315     QFile aFile( aTmpDir.filePath( *aFileIt ) );
316     if ( !aFile.open( QIODevice::ReadOnly ) ){
317       continue;
318     }
319
320     // Incorrect size of file
321     // Do not remove the bad file to have some diagnostic means
322     if ( aFile.read( aBuffer + aCurrPos, *aSIt ) != *aSIt ){
323       aFile.close();      
324       return aStreamFile._retn();
325     }
326
327     aCurrPos += (*aSIt); 
328     aFile.remove();   
329   }
330
331   // Here we should end up with empty aTmpDir
332   // TODO: Handle QDir::rmdir() error status somehow...
333   aTmpDir.rmdir( aTmpPath );
334
335   aBuffer[aBuffSize] = '\0';
336   CORBA::Octet* anOctetBuf =  (CORBA::Octet*)aBuffer;
337   aStreamFile = new Engines::TMPFile(aBuffSize + 1, aBuffSize + 1, anOctetBuf, 1); 
338
339   return aStreamFile._retn();
340 }
341
342 /*!
343   \return Component data type string for this instance of the engine
344 */
345 char* SalomeApp_Engine_i::ComponentDataType()
346 {
347   return CORBA::string_dup( myComponentName.toLatin1().constData() );
348 }
349
350 /*!
351   \return Component version
352 */
353 char* SalomeApp_Engine_i::getVersion()
354 {
355   SalomeApp_Application::ModuleShortInfoList versions = SalomeApp_Application::getVersionInfo();
356   QString version;
357   SalomeApp_Application::ModuleShortInfo version_info;
358   foreach ( version_info, versions ) {
359     if ( SalomeApp_Application::moduleName( version_info.name ) == myComponentName ) {
360       version = version_info.version;
361       break;
362     }
363   }
364   
365   return CORBA::string_dup( version.toLatin1().constData() );
366 }
367
368 /*!
369   \return 
370 */
371 CORBA::ORB_var SalomeApp_Engine_i::orb()
372 {
373   static CORBA::ORB_var _orb;
374
375   if ( CORBA::is_nil( _orb ) ) {
376     Qtx::CmdLineArgs args;
377     ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
378     _orb = init( args.argc(), args.argv() );
379   }
380
381   return _orb;
382 }
383
384 /*!
385   \return 
386 */
387 PortableServer::POA_var SalomeApp_Engine_i::poa()
388 {
389   static PortableServer::POA_var _poa;
390   if ( CORBA::is_nil( _poa ) ){
391     CORBA::Object_var obj = orb()->resolve_initial_references( "RootPOA" );
392     _poa = PortableServer::POA::_narrow( obj );
393   }
394   return _poa;
395 }
396
397 /*!
398   \return 
399 */
400 SALOME_NamingService* SalomeApp_Engine_i::namingService()
401 {
402   static SALOME_NamingService _ns(orb());
403   return &_ns;
404 }
405
406 /*!
407   Internal method, creates a CORBA engine for a light SALOME module
408   with the given "component data type" string,
409   activates it and registers in SALOME naming service with
410   /SalomeAppEngine/comp_data_type path. If the engine is already in the 
411   naming service, simply returns and object reference to it.
412   \param theComponentName - synthetic "component data type" used to identify a given light module
413   \return Object reference to the CORBA engine
414 */
415 CORBA::Object_ptr SalomeApp_Engine_i::EngineForComponent( const char* theComponentName,
416                                                           bool toCreate )
417 {
418   CORBA::Object_var anEngine;
419   if ( !theComponentName || !strlen( theComponentName ) )
420     return anEngine._retn();
421
422   if ( SalomeApp_Application::moduleTitle( theComponentName ).isEmpty() )
423     return anEngine._retn();
424
425   std::string aPath( "/SalomeAppEngine/" );
426   aPath += theComponentName;
427   anEngine = namingService()->Resolve( aPath.c_str() );
428
429   // Activating a new instance of the servant
430   if ( toCreate && CORBA::is_nil( anEngine ) ){
431     try {
432       SalomeApp_Engine_i* aServant    = new SalomeApp_Engine_i( theComponentName );
433       PortableServer::ObjectId_var id = poa()->activate_object( aServant );
434       anEngine = aServant->_this();
435       aServant->_remove_ref();
436       namingService()->Register( anEngine.in(), aPath.c_str() );
437     }
438     catch (CORBA::SystemException&) {
439       INFOS("Caught CORBA::SystemException.");
440     }
441     catch (CORBA::Exception&) {
442       INFOS("Caught CORBA::Exception.");
443     }
444     catch (...) {
445       INFOS("Caught unknown exception.");
446     }
447   }
448
449   return anEngine._retn();
450 }
451
452 /*!
453   \param theComponentName - synthetic "component data type" used to identify a given light module
454   \return IOR string for the CORBA engine for a light SALOME module
455   with the given "component data type" string
456   \sa GetInstance( const char* theComponentName )
457 */
458 std::string SalomeApp_Engine_i::EngineIORForComponent( const char* theComponentName,
459                                                        bool toCreate )
460 {
461   std::string anIOR( "" );
462   CORBA::Object_var anEngine = EngineForComponent( theComponentName, toCreate );
463   if ( !CORBA::is_nil( anEngine ) )
464   {
465     CORBA::String_var objStr = orb()->object_to_string( anEngine.in() );
466     anIOR = std::string( objStr.in() );
467   }
468   return anIOR;
469 }
470
471 /*!
472   \param theComponentName - synthetic "component data type" used to identify a given light module
473   \return A pointer to corresponding C++ engine instance, null means some internal problems.
474   \sa EngineIORForComponent( const char* theComponentName )
475 */
476 SalomeApp_Engine_i* SalomeApp_Engine_i::GetInstance( const char* theComponentName,
477                                                      bool toCreate )
478 {
479   SalomeApp_Engine_i* aServant = 0;
480   CORBA::Object_var anEngine = EngineForComponent( theComponentName, toCreate );
481   if ( !CORBA::is_nil( anEngine ) )
482   {
483     PortableServer::Servant aServantBase = poa()->reference_to_servant( anEngine.in() );
484     aServant = dynamic_cast<SalomeApp_Engine_i*>( aServantBase );
485   } 
486   MESSAGE("SalomeApp_Engine_i::GetInstance(): theComponentName = " <<
487           theComponentName << ", aServant = " << aServant);
488   return aServant;
489 }