1 //                                               -*- C++ -*-
2 /**
3  *  @brief Study keeps all PersistentObjects in a file
4  *
5  *  Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6  *
7  *  This library is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 #include <algorithm>
22 #include "openturns/InterfaceObject.hxx"
23 #include "openturns/Study.hxx"
24 #include "openturns/StorageManager.hxx"
25 #if defined OPENTURNS_HAVE_LIBXML2
26 #include "openturns/XMLStorageManager.hxx"
27 #endif
28 #include "openturns/Exception.hxx"
29 #include "openturns/Catalog.hxx"
30 #include "openturns/PersistentObjectFactory.hxx"
31 
32 BEGIN_NAMESPACE_OPENTURNS
33 
34 
35 
CLASSNAMEINIT(Study)36 CLASSNAMEINIT(Study)
37 
38 /*
39  * Default constructor
40  */
41 Study::Study()
42   : map_(),
43     labelMap_(),
44     p_storageManager_(new StorageManager)
45 {
46   p_storageManager_->setStudy(this);
47 }
48 
49 /*
50  * Parameter constructor
51  */
Study(const FileName & fileName,const UnsignedInteger compressionLevel)52 Study::Study(const FileName & fileName,
53              const UnsignedInteger compressionLevel)
54   : map_(),
55     labelMap_(),
56 #if defined OPENTURNS_HAVE_LIBXML2
57     p_storageManager_(new XMLStorageManager(fileName, compressionLevel))
58 #else
59     p_storageManager_(new StorageManager)
60 #endif /* OPENTURNS_HAVE_LIBXML2 */
61 {
62 #if defined OPENTURNS_HAVE_LIBXML2
63   p_storageManager_->setStudy(this);
64 #else
65   (void)fileName;
66   (void)compressionLevel;
67   throw NotYetImplementedException(HERE) << "Error: no XML support for Study";
68 #endif /* OPENTURNS_HAVE_LIBXML2 */
69 }
70 
71 /* String converter */
__repr__() const72 String Study::__repr__() const
73 {
74   OSS oss;
75   oss << "class=" << getClassName();
76 
77   const char * separator = "  ";
78   for(Map::const_iterator it = map_.begin();
79       it != map_.end();
80       ++it, separator = "\n  ")
81   {
82 #if 0
83     oss << separator << (*it).first << " => " << ((*it).second)->getClassName()
84         << " name='" << ((*it).second)->getName()
85         << "' id=" << ((*it).second)->getId();
86 #endif
87 #if 0
88     if (((*it).second)->getVisibility())
89       oss << separator << (*it).first << " => id=" << ((*it).second)->getId()
90           << " " << ((*it).second)->__repr__();
91 #endif
92 #if 1
93     oss << separator << (*it).first << " => " << (((*it).second)->getVisibility() ? "*" : " ") << " id=" << ((*it).second)->getId()
94         << " " << ((*it).second)->__repr__();
95 #endif
96   }
97 
98   oss << "\n";
99   separator = "  ";
100   for(LabelMap::const_iterator it = labelMap_.begin();
101       it != labelMap_.end();
102       ++it, separator = "\n  ")
103   {
104     oss << separator << "'" << (*it).first << "' is aliased to " << (*it).second;
105   }
106   return oss;
107 }
108 
109 /* String converter */
__str__(const String &) const110 String Study::__str__(const String & ) const
111 {
112   OSS oss(false);
113   const char * separator = "  ";
114   for(Map::const_iterator it = map_.begin(); it != map_.end(); ++it, separator = "\n  ")
115   {
116     oss << separator << (*it).first << " => " << ((*it).second)->getClassName() << "\n"
117         << ((*it).second)->__str__( String( 7 + int( ((*it).first != 0 ? log10(static_cast<double>((*it).first)) : 1) ), ' ') );
118   }
119   oss << "\n";
120   separator = "  ";
121   for(LabelMap::const_iterator it = labelMap_.begin();
122       it != labelMap_.end();
123       ++it, separator = "\n  ")
124   {
125     oss << separator << "'" << (*it).first << "' is aliased to " << (*it).second;
126   }
127   return oss;
128 }
129 
130 /* This method saves the study through the storage manager */
save()131 void Study::save()
132 {
133   assert(p_storageManager_);
134   StorageManager & mgr = *p_storageManager_;
135 
136   mgr.initialize( SaveAction() );
137 
138   for(LabelMap::const_iterator it = labelMap_.begin();
139       it != labelMap_.end(); ++it)
140   {
141     mgr.save(*map_[ it->second ], it->first, true);
142   }
143 
144   for(Map::const_iterator it = map_.begin();
145       it != map_.end(); ++it)
146   {
147     mgr.save(*(it->second), "", true);
148   }
149 
150   mgr.write();
151   mgr.finalize( SaveAction() );
152 }
153 
154 /* This method reloads the study from the storage manager */
load()155 void Study::load()
156 {
157   assert(p_storageManager_);
158   StorageManager & mgr = *p_storageManager_;
159 
160   mgr.initialize( LoadAction() );
161   mgr.read();
162   mgr.load(*this);
163   mgr.finalize( LoadAction() );
164 
165   cleanUnvisibleObject();
166   translateId();
167 }
168 
169 /* This method purges the study from the reloaded objects that are tagged unvisible */
cleanUnvisibleObject()170 void Study::cleanUnvisibleObject()
171 {
172   Map newMap;
173   for(Map::const_iterator it = map_.begin(); it != map_.end(); ++it)
174   {
175     if (it->second->getVisibility())
176       newMap[ it->second->getShadowedId() ] = it->second;
177   }
178   map_ = newMap;
179 }
180 
181 /* This method performs the translation of ids after a study load */
translateId()182 void Study::translateId()
183 {
184   std::map<Id, Id> translationTable;
185 
186   Map newMap;
187   for(Map::const_iterator it = map_.begin(); it != map_.end(); ++it)
188   {
189     translationTable[ it->first ] = it->second->getId();
190     newMap[ it->second->getId() ] = it->second;
191   }
192 
193   LabelMap newLabelMap;
194   for(LabelMap::const_iterator it = labelMap_.begin(); it != labelMap_.end(); ++it)
195     newLabelMap[ it->first ] = translationTable[ it->second ];
196 
197   map_      = newMap;
198   labelMap_ = newLabelMap;
199 }
200 
201 
202 
203 
204 
205 
206 /* Query if object is stored in study */
hasObject(Id id) const207 Bool Study::hasObject(Id id) const
208 {
209   Map::const_iterator it = map_.find( id );
210   return ( it != map_.end() );
211 }
212 
213 /* Get object whose id is given */
getObject(Id id) const214 Study::MapElement Study::getObject(Id id) const
215 {
216   MapElement element;
217   Map::const_iterator it = map_.find( id );
218   if (it != map_.end())
219   {
220     element = (*it).second;
221   }
222   return element;
223 }
224 
225 
226 
227 
228 /* Query if object is stored in study */
hasObject(const String & label) const229 Bool Study::hasObject(const String & label) const
230 {
231   if (label.empty()) return false;
232   LabelMap::const_iterator it = labelMap_.find( label );
233   return (it == labelMap_.end()) ? false : hasObject( it->second );
234 }
235 
236 /* Get object whose id is given */
getObject(const String & label) const237 Study::MapElement Study::getObject(const String & label) const
238 {
239   LabelMap::const_iterator it_label = labelMap_.find( label );
240   if (it_label == labelMap_.end()) throw InvalidArgumentException(HERE) << "No object with label '" << label << "' in study";
241   return getObject( it_label->second );
242 }
243 
244 
245 
246 
247 
248 /* Local class for the following method. Should have been declared inside the method but find_if crashes */
249 struct element_whose_class_and_name_are
250 {
251   const String & className_;
252   const String & name_;
element_whose_class_and_name_areelement_whose_class_and_name_are253   element_whose_class_and_name_are(const String & className, const String & name) : className_(className), name_(name) {}
operator ()element_whose_class_and_name_are254   Bool operator()(const Study::Map::value_type & element) const
255   {
256     return (element.second->getClassName() == className_) && (element.second->getName() == name_);
257   }
258 };
259 
260 /* Get object whose class and name are given */
getObjectByName(const String & className,const String & name) const261 Study::MapElement Study::getObjectByName(const String & className, const String & name) const
262 {
263   MapElement element;
264   Map::const_iterator it = std::find_if(map_.begin(), map_.end(), element_whose_class_and_name_are(className, name));
265   if (it != map_.end())
266   {
267     element = (*it).second;
268   }
269   return element;
270 }
271 
272 
273 /* Fill an object with one got from study */
fillObjectByName(PersistentObject & po,const String & name) const274 void Study::fillObjectByName(PersistentObject & po, const String & name) const
275 {
276   MapElement element = getObjectByName(po.getClassName(), name);
277   if (! element) throw InvalidArgumentException(HERE) << "No object of name " << name << " in study";
278   Catalog::Get(po.getClassName()).assign(po, *element);
279 }
280 
fillObjectByName(InterfaceObject & io,const String & name) const281 void Study::fillObjectByName(InterfaceObject  & io, const String & name) const
282 {
283   MapElement element = getObjectByName(io.getImplementationAsPersistentObject()->getClassName(), name);
284   if (! element) throw InvalidArgumentException(HERE) << "No object of name " << name << " in study";
285   io.setImplementationAsPersistentObject(element);
286 }
287 
fillObject(Id id,PersistentObject & po) const288 void Study::fillObject(Id id, PersistentObject & po) const
289 {
290   MapElement element = getObject(id);
291   if (! element) throw InvalidArgumentException(HERE) << "No object of id " << id << " in study";
292   Catalog::Get(po.getClassName()).assign(po, *element);
293 }
294 
fillObject(Id id,InterfaceObject & io) const295 void Study::fillObject(Id id, InterfaceObject & io) const
296 {
297   MapElement element = getObject(id);
298   if (! element) throw InvalidArgumentException(HERE) << "No object of id " << id << " in study";
299   io.setImplementationAsPersistentObject(element);
300 }
301 
fillObject(const String & label,PersistentObject & po) const302 void Study::fillObject(const String & label, PersistentObject & po) const
303 {
304   MapElement element = getObject(label);
305   if (! element) throw InvalidArgumentException(HERE) << "No object labelled '" << label << "' in study";
306   Catalog::Get(po.getClassName()).assign(po, *element);
307 }
308 
fillObject(const String & label,InterfaceObject & io) const309 void Study::fillObject(const String & label, InterfaceObject & io) const
310 {
311   MapElement element = getObject(label);
312   if (! element) throw InvalidArgumentException(HERE) << "No object labelled '" << label << "' in study";
313   io.setImplementationAsPersistentObject(element);
314 }
315 
316 
317 /* Storage manager accessor */
setStorageManager(const StorageManager & smgr)318 void Study::setStorageManager(const StorageManager & smgr)
319 {
320   p_storageManager_.reset(smgr.clone());
321   p_storageManager_->setStudy(this);
322 }
323 
324 /* Storage manager accessor */
getStorageManager() const325 Study::StorageManagerImplementation Study::getStorageManager() const
326 {
327   return p_storageManager_;
328 }
329 
330 /* Define a label for an object */
defineLabel(Id id,const String & label)331 void Study::defineLabel(Id id, const String & label)
332 {
333   if (! label.empty()) labelMap_[ label ] = id;
334 }
335 
336 
337 /* Define the visibility of an object */
defineVisibility(Id id,Bool visible)338 void Study::defineVisibility(Id id, Bool visible)
339 {
340   MapElement elt = getObject( id );
341   elt->setVisibility( visible );
342 }
343 
344 /* Add a PersistentObject to the study */
add(const InterfaceObject & io)345 void Study::add(const InterfaceObject & io)
346 {
347   map_[ io.getId() ] = io.getImplementationAsPersistentObject();
348 }
349 
350 /* Add a PersistentObject to the study */
add(const String & label,const InterfaceObject & io,Bool force)351 void Study::add(const String & label, const InterfaceObject & io, Bool force)
352 {
353   if ( hasObject( label ) )
354   {
355     if (force) remove( label );
356     else throw InvalidArgumentException(HERE) << "Label '" << label << "' already defined in study. Use 'force = true' to remove previously saved element before saving this one";
357   }
358   map_[ io.getId() ] = io.getImplementationAsPersistentObject();
359   defineLabel( io.getId(), label );
360 }
361 
362 /* Remove a PersistentObject from the study */
remove(const InterfaceObject & io)363 void Study::remove(const InterfaceObject & io)
364 {
365   Map::iterator it = map_.find( io.getId() );
366   map_.erase(it);
367 }
368 
369 /* Remove a PersistentObject from the study */
remove(const String & label)370 void Study::remove(const String & label)
371 {
372   LabelMap::iterator it_label = labelMap_.find( label );
373   if (it_label == labelMap_.end()) throw InvalidArgumentException(HERE) << "No object with label '" << label << "' in study";
374   Map::iterator it_obj = map_.find( it_label->second );
375   map_.erase(it_obj);
376   labelMap_.erase( it_label );
377 }
378 
379 /* Add a PersistentObject to the study */
add(const PersistentObject & po)380 void Study::add(const PersistentObject & po)
381 {
382   add(po.clone());
383 }
384 
385 /* Add a PersistentObject to the study */
add(const String & label,const PersistentObject & po,Bool force)386 void Study::add(const String & label, const PersistentObject & po, Bool force)
387 {
388   add(label, po.clone(), force);
389 }
390 
391 /* Add a PersistentObject to the study (any map) */
add(const PersistentObject * po)392 void Study::add(const PersistentObject * po)
393 {
394   if (! po) throw InvalidArgumentException(HERE) << "Null pointer passed to method";
395   map_[ po->getShadowedId() ] = const_cast<PersistentObject *>(po);
396 }
397 
398 /* Add a PersistentObject to the study (any map) */
add(const String & label,const PersistentObject * po,Bool force)399 void Study::add(const String & label, const PersistentObject * po, Bool force)
400 {
401   if (! po) throw InvalidArgumentException(HERE) << "Null pointer passed to method";
402   if ( hasObject( label ) )
403   {
404     if (force) remove( label );
405     else throw InvalidArgumentException(HERE) << "Label '" << label << "' already defined in study. Use 'force = true' to remove previously saved element before saving this one";
406   }
407   map_[ po->getShadowedId() ] = const_cast<PersistentObject *>(po);
408   defineLabel( po->getShadowedId(), label );
409 }
410 
411 /* Print all the labels in the study */
printLabels() const412 String Study::printLabels() const
413 {
414   OSS oss;
415   String separator("");
416   for(LabelMap::const_iterator it = labelMap_.begin(); it != labelMap_.end(); ++it, separator = ";") oss << separator << (*it).first;
417   return oss;
418 }
419 
420 END_NAMESPACE_OPENTURNS
421