fw4spl
Reader.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2017.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "fwAtomsBoostIO/Reader.hpp"
8 
9 #include "fwAtomsBoostIO/Writer.hpp"
10 
11 #include <fwAtoms/Blob.hpp>
12 #include <fwAtoms/Boolean.hpp>
13 #include <fwAtoms/Map.hpp>
14 #include <fwAtoms/Numeric.hpp>
15 #include <fwAtoms/Numeric.hxx>
16 #include <fwAtoms/Object.hpp>
17 #include <fwAtoms/Sequence.hpp>
18 #include <fwAtoms/String.hpp>
19 
20 #include <fwTools/UUID.hpp>
21 
22 #include <fwZip/IReadArchive.hpp>
23 
24 #include <boost/filesystem/operations.hpp>
25 #include <boost/property_tree/json_parser.hpp>
26 #include <boost/property_tree/xml_parser.hpp>
27 
28 #include <sstream>
29 
30 namespace fwAtomsBoostIO
31 {
32 
33 //------------------------------------------------------------------------------
34 
35 size_t countSubAtoms(const ::boost::property_tree::ptree& pt)
36 {
37  size_t nb = 0;
38  for(const ::boost::property_tree::ptree::value_type& v : pt)
39  {
40  if(
41  (v.first == "numeric") ||
42  (v.first == "string") ||
43  (v.first == "boolean") ||
44  (v.first == "sequence") ||
45  (v.first == "map") ||
46  (v.first == "object") ||
47  (v.first == "blob")
48  )
49  {
50  nb++;
51  }
52  nb += countSubAtoms(v.second);
53  }
54  return nb;
55 }
56 
57 //-----------------------------------------------------------------------------
58 
60 {
61 
62  typedef std::map< std::string, ::fwAtoms::Base::sptr > AtomCacheType;
63 
64  AtomCacheType m_cache;
65  const ::boost::property_tree::ptree& m_root;
66  ::fwZip::IReadArchive::sptr m_archive;
67 
68 //-----------------------------------------------------------------------------
69 
70  PTreeVisitor(const ::boost::property_tree::ptree& pt, const ::fwZip::IReadArchive::sptr& archive) :
71  m_root(pt),
72  m_archive(archive)
73  {
74  }
75 
76 //-----------------------------------------------------------------------------
77 
78  AtomCacheType::mapped_type hitCache(const AtomCacheType::key_type& path) const
79  {
80  AtomCacheType::const_iterator iter = m_cache.find(path);
81  if(iter != m_cache.end())
82  {
83  return iter->second;
84  }
85  return AtomCacheType::mapped_type();
86  }
87 
88 //-----------------------------------------------------------------------------
89 
90  void cache(const std::string& ptpath, const AtomCacheType::mapped_type& atom)
91  {
92  m_cache.insert( AtomCacheType::value_type( ptpath, atom ) );
93  }
94 
95 //-----------------------------------------------------------------------------
96 
97  ::fwAtoms::Boolean::sptr getBoolean(const ::boost::property_tree::ptree& pt, const std::string& ptpath)
98  {
99  ::fwAtoms::Boolean::sptr atom = ::fwAtoms::Boolean::New(pt.get<std::string>("boolean.value"));
100  this->cache(ptpath, atom);
101  return atom;
102  }
103 
104 //-----------------------------------------------------------------------------
105 
106  ::fwAtoms::Numeric::sptr getNumeric(const ::boost::property_tree::ptree& pt, const std::string& ptpath )
107  {
108  ::fwAtoms::Numeric::sptr atom = ::fwAtoms::Numeric::New(pt.get<std::string>("numeric.value"));
109  this->cache(ptpath, atom);
110  return atom;
111  }
112 
113 //-----------------------------------------------------------------------------
114 
115  ::fwAtoms::String::sptr getString(const ::boost::property_tree::ptree& pt, const std::string& ptpath )
116  {
117  ::fwAtoms::String::sptr atom = ::fwAtoms::String::New(pt.get<std::string>("string.value"));
118  this->cache(ptpath, atom);
119  return atom;
120  }
121 
122 //-----------------------------------------------------------------------------
123 
124  ::fwAtoms::Sequence::sptr getSequence(const ::boost::property_tree::ptree& pt, const std::string& ptpath )
125  {
126  ::fwAtoms::Sequence::sptr atom = ::fwAtoms::Sequence::New();
127  this->cache(ptpath, atom);
128 
129  // We cannot assume that boost will give us the values in the right order (see property_tree documentation)
130  // Therefore, we need to sort the subatoms by key prior to adding them to the sequence
131  std::vector<const ::boost::property_tree::ptree::value_type*> elems;
132  std::for_each(pt.get_child("sequence").begin(), pt.get_child("sequence").end(),
133  [&](::boost::property_tree::ptree::value_type const& elem)
134  {
135  elems.push_back(&elem);
136  });
137  std::sort(elems.begin(), elems.end(),
138  [&](const ::boost::property_tree::ptree::value_type* s1,
139  const ::boost::property_tree::ptree::value_type* s2)
140  {
141  const unsigned long n1 = std::stoul(s1->first),
142  n2 = std::stoul(s2->first);
143  return n1 <= n2;
144  });
145 
146  // Elements are now sorted, add them to the sequence
147  for( const ::boost::property_tree::ptree::value_type* val : elems )
148  {
149  std::string subPath = ptpath + (ptpath.empty() ? "" : ".") + "sequence." + val->first;
150  ::fwAtoms::Base::sptr subAtom = this->visit(val->second, subPath );
151  atom->push_back( subAtom );
152  }
153  return atom;
154  }
155 
156 //-----------------------------------------------------------------------------
157 
158  ::fwAtoms::Map::sptr getMap(const ::boost::property_tree::ptree& pt, const std::string& ptpath )
159  {
160  ::fwAtoms::Map::sptr atom = ::fwAtoms::Map::New();
161  this->cache(ptpath, atom);
162 
163  for( const ::boost::property_tree::ptree::value_type& val : pt.get_child("map") )
164  {
165  std::string subPath = ptpath + (ptpath.empty() ? "" : ".") + "map." + val.first + ".value";
166 
167  ::boost::property_tree::ptree mapChild = val.second;
168  ::boost::property_tree::ptree value = mapChild.get_child("value");
169 
170  ::fwAtoms::Base::sptr subAtom = this->visit( value, subPath );
171 
172  std::string key = mapChild.get<std::string>("key");
173  atom->insert( key, subAtom );
174  }
175  return atom;
176  }
177 
178 //-----------------------------------------------------------------------------
179 
180  ::fwAtoms::Object::sptr getObject(const ::boost::property_tree::ptree& pt, const std::string& ptpath )
181  {
182  using ::boost::property_tree::ptree;
183  ::fwAtoms::Object::sptr atom = ::fwAtoms::Object::New();
184  this->cache(ptpath, atom);
185 
186  const ptree& metaInfosTree = pt.get_child("object.meta_infos");
187  const ptree& attributesTree = pt.get_child("object.attributes");
188 
189  ::fwAtoms::Object::MetaInfosType metaInfos;
190  for( const ptree::value_type& val : metaInfosTree )
191  {
192  ::boost::property_tree::ptree item = val.second;
193 
194  ::fwAtoms::Object::MetaInfosType::value_type value(
195  item.get<std::string>("key"), item.get<std::string>("value") );
196  metaInfos.insert( value );
197  }
198  atom->setMetaInfos(metaInfos);
199 
200  ::fwAtoms::Object::AttributesType attributes;
201  for( const ptree::value_type& val : attributesTree )
202  {
203  std::string subPath = ptpath + (ptpath.empty() ? "" : ".")+ "object.attributes." + val.first;
204  ::fwAtoms::Base::sptr subAtom = this->visit(val.second, subPath );
205  ::fwAtoms::Object::AttributesType::value_type value(val.first, subAtom);
206  attributes.insert( value );
207  }
208  atom->setAttributes(attributes);
209 
210  // Managing object with no id
211  if(atom->getMetaInfo("ID_METAINFO").empty())
212  {
213  atom->setMetaInfo("ID_METAINFO", ::fwTools::UUID::generateUUID());
214  }
215 
216  return atom;
217  }
218 
219 //-----------------------------------------------------------------------------
220 
222  {
223  public:
224  AtomsBoostIOReadStream(const ::fwZip::IReadArchive::sptr& archive, const boost::filesystem::path& path) :
225  m_archive(archive),
226  m_path(path)
227  {
228  }
229 
230  protected:
231 
232  SPTR(std::istream) get()
233  {
234  return m_archive->getFile(m_path);
235  }
236 
237  ::fwZip::IReadArchive::sptr m_archive;
238  ::boost::filesystem::path m_path;
239  };
240 
241  //------------------------------------------------------------------------------
242 
243  ::fwAtoms::Blob::sptr getBlob(const ::boost::property_tree::ptree& pt, const std::string& ptpath)
244  {
245  ::fwAtoms::Blob::sptr atom = ::fwAtoms::Blob::New();
246  ::fwMemory::BufferObject::sptr buffObj = ::fwMemory::BufferObject::New();
247  atom->setBufferObject(buffObj);
248 
249  this->cache(ptpath, atom);
250 
251  const std::string bufType = pt.get<std::string>("blob.buffer_type");
252 
253  if(bufType == "raw")
254  {
255  size_t buffSize = pt.get<size_t>("blob.buffer_size");
256  if(buffSize > 0)
257  {
258  const ::boost::filesystem::path bufFile = pt.get<std::string>("blob.buffer");
259  ::boost::filesystem::path sourceFile = "";
260  ::fwMemory::FileFormatType format = ::fwMemory::OTHER;
261 
262  if( ::boost::filesystem::is_directory(m_archive->getArchivePath()))
263  {
264  sourceFile = m_archive->getArchivePath() / bufFile;
265  format = ::fwMemory::RAW;
266  }
267 
268  buffObj->setIStreamFactory( std::make_shared< AtomsBoostIOReadStream >(m_archive->clone(), bufFile),
269  buffSize, sourceFile, format);
270  }
271  }
272  else
273  {
274  FW_RAISE("Buffer type '" << bufType << "' unknown.");
275  }
276  return atom;
277  }
278 
279 //-----------------------------------------------------------------------------
280 
281  ::fwAtoms::Base::sptr visit(const ::boost::property_tree::ptree& pt, std::string ptpath = "")
282  {
283  if(pt.empty())
284  {
285  return ::fwAtoms::Base::sptr();
286  }
287 
288  ::fwAtoms::Base::sptr atom = this->hitCache(ptpath);
289 
290  if(atom)
291  {
292  return atom;
293  }
294 
295  if(pt.count("numeric") == 1 )
296  {
297  atom = this->getNumeric( pt, ptpath );
298  }
299  else if(pt.count("string") == 1)
300  {
301  atom = this->getString( pt, ptpath );
302  }
303  else if(pt.count("boolean") == 1)
304  {
305  atom = this->getBoolean( pt, ptpath );
306  }
307  else if(pt.count("sequence") == 1)
308  {
309  atom = this->getSequence( pt, ptpath );
310  }
311  else if(pt.count("map") == 1)
312  {
313  atom = this->getMap( pt, ptpath );
314  }
315  else if(pt.count("object") == 1)
316  {
317  atom = this->getObject( pt, ptpath );
318  }
319  else if(pt.count("blob") == 1)
320  {
321  atom = this->getBlob( pt, ptpath );
322  }
323  else if(pt.count("ref") == 1)
324  {
325  std::string ref = pt.get<std::string>("ref");
326  const ::boost::property_tree::ptree& refPt = m_root.get_child(ref);
327  atom = this->visit( refPt, ref );
328  }
329  else
330  {
331  FW_RAISE("Unknown element found in archive.");
332  }
333 
334  return atom;
335  }
336 
337 //-----------------------------------------------------------------------------
338 
339  ::fwAtoms::Base::sptr visit()
340  {
341  return this->visit(m_root);
342  }
343 
344 };
345 
346 //-----------------------------------------------------------------------------
347 
348 ::fwAtoms::Base::sptr Reader::read( const ::fwZip::IReadArchive::sptr& archive,
349  const ::boost::filesystem::path& rootFilename,
350  FormatType format ) const
351 {
352  ::boost::property_tree::ptree root;
353  ::fwAtoms::Base::sptr atom;
354 
355  SPTR(std::istream) in = archive->getFile(rootFilename);
356  if(format == JSON)
357  {
358  ::boost::property_tree::json_parser::read_json(*in, root);
359  }
360  else if(format == XML)
361  {
362  ::boost::property_tree::xml_parser::read_xml(*in, root);
363  }
364  else
365  {
366  FW_RAISE("This kind of extension is not supported");
367  }
368 
369  typedef ::boost::property_tree::ptree::const_assoc_iterator PtreeItType;
370  PtreeItType hasVersionsIt = root.find("versions");
371  FW_RAISE_IF("Failed to read file '" << rootFilename.string() << "':\nno versions found in specified file.",
372  hasVersionsIt == root.not_found());
373 
374  ::boost::property_tree::ptree versions = root.get_child("versions");
375 
376  PtreeItType hasAtomsVersionsIt = versions.find(Writer::s_ATOMS_VERSION_KEY);
377  FW_RAISE_IF("Failed to read file '" << rootFilename.string() << "':\nno atoms version found in specified file.",
378  hasAtomsVersionsIt == versions.not_found());
379 
380  PtreeItType hasWriterVersionsIt = versions.find(Writer::s_WRITER_VERSION_KEY);
381  FW_RAISE_IF("Failed to read file '" << rootFilename.string() << "':\nno writer version found in specified file",
382  hasWriterVersionsIt == versions.not_found());
383 
384  const std::string& atomsVersion = versions.get< std::string >(Writer::s_ATOMS_VERSION_KEY);
385  const std::string& writerVersion = versions.get< std::string >(Writer::s_WRITER_VERSION_KEY);
386 
387  FW_RAISE_IF(
388  "Failed to read file '" << rootFilename.string() << "':\n"
389  << "Detected file version is '" << writerVersion << "'"
390  << " whereas current version is '" << Writer::s_VERSION << "'",
391  Writer::s_VERSION != writerVersion);
392 
393  FW_RAISE_IF("Failed to read file '" << rootFilename.string() << "':\n"
394  << "Detected atoms version is '" << atomsVersion << "'"
395  << " whereas current version is '" << ::fwAtoms::Base::s_VERSION << "'",
396  ::fwAtoms::Base::s_VERSION != atomsVersion);
397 
398  PTreeVisitor visitor(root, archive);
399  atom = visitor.visit();
400 
401  return atom;
402 }
403 
404 }
405 
#define SPTR(_cls_)
The namespace fwAtomsBoostIO contains atom reader and writer.
static FWATOMS_API String::sptr New(std::string value)
Construct a new Object represented a string.
static FWATOMSBOOSTIO_API const std::string s_ATOMS_VERSION_KEY
Defines key to retrieve fwAtoms version from file.
Definition: Writer.hpp:40
static FWATOMSBOOSTIO_API const std::string s_WRITER_VERSION_KEY
Defines key to retrieve writer version from file.
Definition: Writer.hpp:43
static Numeric::sptr New(T value)
Build a new numeric type.
Definition: Numeric.hxx:77
static FWATOMSBOOSTIO_API const std::string s_VERSION
Defines writer version.
Definition: Writer.hpp:37
static FWATOMS_API const std::string s_VERSION
Defines fwAtoms version.
static FWATOMS_API Boolean::sptr New(std::string value)
Construct an object storing a bool value.
static FWTOOLS_API UUIDType generateUUID()
Return a new extended UUID;.
Definition: UUID.cpp:114
static FWATOMS_API Blob::sptr New(::fwMemory::BufferObject::sptr buffer)
create a new Blob shared ptr.
Definition: Blob.cpp:17