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