fw4spl
DictionaryReader.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 #define FUSION_MAX_VECTOR_SIZE 20
8 
9 #include <iostream>
10 #include <fstream>
11 #include <vector>
12 #include <sstream>
13 
14 #ifdef DEBUG
15 static std::stringstream spiritDebugStream;
16  #define BOOST_SPIRIT_DEBUG_OUT spiritDebugStream
17  #define BOOST_SPIRIT_DEBUG
18 #endif
19 
20 #include <boost/algorithm/string.hpp>
21 #include <boost/algorithm/string/trim.hpp>
22 #include <boost/algorithm/string/split.hpp>
23 #include <boost/algorithm/string/detail/case_conv.hpp>
24 
25 #include <boost/spirit/include/qi.hpp>
26 #include <boost/spirit/include/qi_eol.hpp>
27 #include <boost/spirit/include/phoenix.hpp>
28 #include <boost/spirit/include/phoenix_core.hpp>
29 #include <boost/spirit/include/phoenix_operator.hpp>
30 #include <boost/spirit/include/phoenix_bind.hpp>
31 #include <boost/spirit/include/phoenix_statement.hpp>
32 #include <boost/spirit/include/phoenix_stl.hpp>
33 
34 #include <boost/fusion/include/adapt_struct.hpp>
35 
36 #include <boost/fusion/container.hpp>
37 #include <boost/fusion/container/vector/vector.hpp>
38 #include <boost/fusion/include/vector.hpp>
39 
40 #include <fwCore/exceptionmacros.hpp>
41 
42 #include <fwData/Color.hpp>
43 #include <fwData/StructureTraitsDictionary.hpp>
44 #include <fwData/StructureTraits.hpp>
45 #include <fwData/StructureTraitsHelper.hpp>
46 
47 #include <fwRuntime/operations.hpp>
48 
49 #include "fwDataIO/reader/DictionaryReader.hpp"
50 #include "fwDataIO/reader/registry/macros.hpp"
51 
52 fwDataIOReaderRegisterMacro( ::fwDataIO::reader::DictionaryReader );
53 
54 namespace fwDataIO
55 {
56 
57 struct line
58 {
59  std::string type;
60  double red;
61  double green;
62  double blue;
63  double alpha;
64  std::string catgegory;
65  std::string organClass;
66  std::string attachment;
67  std::string nativeExp;
68  std::string nativeExpGeo;
69  std::string anatomicRegion;
70  std::string propertyCategory;
71  std::string propertyType;
72 };
73 }
74 
75 BOOST_FUSION_ADAPT_STRUCT(
77  (std::string, type)
78  (double, red)
79  (double, green)
80  (double, blue)
81  (double, alpha)
82  (std::string, catgegory)
83  (std::string, organClass)
84  (std::string, attachment)
85  (std::string, nativeExp)
86  (std::string, nativeExpGeo)
87  (std::string, anatomicRegion)
88  (std::string, propertyCategory)
89  (std::string, propertyType)
90  )
91 
92 //------------------------------------------------------------------------------
93 
94 inline std::string trim ( std::string& s )
95 {
96  return ::boost::algorithm::trim_copy(s);
97 }
98 
99 //------------------------------------------------------------------------------
100 
102 std::string reformatString(std::string& expr)
103 {
104  std::string trimStr = ::boost::algorithm::trim_copy(expr);
105  std::string result = ::boost::algorithm::to_upper_copy(trimStr.substr(0, 1))
106  + ::boost::algorithm::to_lower_copy(trimStr.substr(1));
107  return (result);
108 }
109 
110 //------------------------------------------------------------------------------
112 
113 template< typename MapType >
114 std::string getValues(const MapType& m)
115 {
116  std::stringstream str;
117  typedef typename MapType::const_iterator const_iterator;
118  const_iterator iter = m.begin();
119  str << "( " << iter->first;
120  for(; iter != m.end(); ++iter )
121  {
122  str << ", " << iter->first;
123  }
124  str << ") ";
125  return str.str();
126 }
127 //------------------------------------------------------------------------------
128 
129 namespace fwDataIO
130 {
131 
132 namespace qi = boost::spirit::qi;
133 namespace ascii = boost::spirit::ascii;
134 
135 template <typename Iterator>
136 struct line_parser : qi::grammar<Iterator, std::vector <line>() >
137 {
138  line_parser() :
139  line_parser::base_type(lines)
140  {
141  using qi::int_;
142  using qi::lit;
143  using qi::_1;
144  using qi::double_;
145  using qi::blank;
146  using qi::alnum;
147  using qi::omit;
148  using ascii::char_;
149  using boost::spirit::qi::eol;
150  using boost::spirit::qi::eoi;
151  using boost::phoenix::construct;
152  namespace phx = boost::phoenix;
153 
154  error.clear();
155 
156  lines = +( line[phx::push_back(qi::_val, qi::_1)] | comment ) >> eoi;
157  comment = *blank >> lit('#') >> *(char_- eol)>> +qi::eol;
158 
159  line = trimmedString >> lit(';')
160  >> omit[*blank]>> lit('(')
161  >> dbl >> lit(',')
162  >> dbl >> lit(',')
163  >> dbl >> lit(',')
164  >> dbl
165  >> lit(')') >> omit[*blank]
166  >> lit(';')
167  >> stringSet >> lit(';')
168  >> trimmedString >> lit(';')
169  >> trimmedString >> lit(';')
170  >> trimmedStringExpr >> lit(';')
171  >> trimmedStringExpr >> lit(';')
172  >> trimmedString >> lit(';')
173  >> trimmedString >> lit(';')
174  >> trimmedString
175  >> +qi::eol;
176 
177  trimmedString = str[qi::_val = phx::bind(trim, qi::_1)];
178  str = *( (alnum|char_("_"))[qi::_val += qi::_1] | blank[qi::_val += " "]);
179 
180  trimmedStringExpr = stringExpr[qi::_val = phx::bind(trim, qi::_1)];
181  stringExpr = *( (alnum|char_("()_,.+-"))[qi::_val += qi::_1] | blank[qi::_val += " "] );
182 
183  stringSet = stringWithComma[qi::_val = phx::bind(trim, qi::_1)];
184  stringWithComma = *( (alnum| char_(",_"))[qi::_val += qi::_1] | blank[qi::_val += " "] );
185 
186  dbl = omit[*blank] >> double_ >> omit[*blank];
187 
188  #ifdef BOOST_SPIRIT_DEBUG
189  BOOST_SPIRIT_DEBUG_NODE(comment);
190  BOOST_SPIRIT_DEBUG_NODE(trimmedString);
191  BOOST_SPIRIT_DEBUG_NODE(trimmedStringExpr);
192  BOOST_SPIRIT_DEBUG_NODE(stringSet);
193  BOOST_SPIRIT_DEBUG_NODE(dbl);
194  BOOST_SPIRIT_DEBUG_NODE(line);
195  BOOST_SPIRIT_DEBUG_NODE(lines);
196  SLM_DEBUG(spiritDebugStream.str());
197  spiritDebugStream.str( std::string() );
198  #endif
199 
200  qi::on_error< qi::fail>
201  (
202  line
203  , phx::ref( (std::ostream&)error )
204  << phx::val("Error! Expecting ")
205  << qi::_4 // what failed?
206  << phx::val(" here: \"")
207  << phx::construct<std::string>(qi::_3, qi::_2) // iterators to error-pos, end
208  << phx::val("\"")
209  << std::endl
210  );
211 
212  }
213 
214  qi::rule<Iterator, double()> dbl;
215  qi::rule<Iterator, std::string()> str;
216  qi::rule<Iterator, std::string()> stringExpr;
217  qi::rule<Iterator, std::string()> stringWithComma;
218 
219  qi::rule<Iterator, std::string()> comment;
220  qi::rule<Iterator, std::string()> trimmedStringExpr;
221  qi::rule<Iterator, std::string()> trimmedString;
222  qi::rule<Iterator, std::string()> stringSet;
223 
224  qi::rule<Iterator, ::fwDataIO::line()> line;
225  qi::rule<Iterator, std::vector< ::fwDataIO::line >() > lines;
226  std::stringstream error;
227 };
228 
229 namespace reader
230 {
231 //------------------------------------------------------------------------------
232 
233 template <typename Iterator>
234 std::pair<bool, std::string> parse(Iterator first, Iterator last, std::string& buf, std::vector<fwDataIO::line>& lines)
235 {
236  using boost::spirit::ascii::space;
237  using boost::spirit::ascii::blank;
238  using boost::spirit::qi::eol;
239 
240  using boost::spirit::qi::phrase_parse;
241 
242  typedef std::string::const_iterator iterator_type;
243  typedef ::fwDataIO::line_parser<iterator_type> line_parser;
244 
245  iterator_type iter = buf.begin();
246  iterator_type end = buf.end();
247 
248  line_parser grammar; // Our grammar
249 
250  bool result = phrase_parse(iter, end, grammar, space - blank - eol, lines);
251  bool success = result && (iter == end);
252  std::string msg = grammar.error.str();
253  return std::make_pair( success, msg );
254 }
255 
256 //------------------------------------------------------------------------------
257 
258 DictionaryReader::DictionaryReader(::fwDataIO::reader::IObjectReader::Key key) :
259  ::fwData::location::enableSingleFile< IObjectReader >(this)
260 {
261 }
262 
263 //------------------------------------------------------------------------------
264 
266 {
267 }
268 
269 //------------------------------------------------------------------------------
270 
272 {
273  SLM_TRACE_FUNC();
274  assert( std::dynamic_pointer_cast< ::fwData::location::SingleFile >(m_location) );
275  ::boost::filesystem::path path = std::dynamic_pointer_cast< ::fwData::location::SingleFile >(m_location)->getPath();
276 
277  OSLM_INFO( "[DictionaryReader::read] dictionary file: " << path.string());
278  SLM_ASSERT("Empty path for dictionary file", !path.empty());
279 
280  // Reading of the file
281  size_t length;
282  std::string buf;
283  std::ifstream file;
284  file.open(path.string().c_str(), std::ios::binary );
285 
286  std::string errorOpen = "Unable to open " + path.string();
287  FW_RAISE_IF(errorOpen, !file.is_open());
288 
289  file.seekg(0, std::ios::end);
290  length = file.tellg();
291  file.seekg(0, std::ios::beg);
292 
293  buf.resize(length);
294  char* buffer = &buf[0];
295 
296  file.read(buffer, length);
297  file.close();
298 
299  std::vector < ::fwDataIO::line > dicolines;
300  std::pair<bool, std::string> result = parse(buffer, buffer+length, buf, dicolines);
301 
302  std::string error = "Unable to parse " + path.string() + " : Bad file format.Error : " + result.second;
303  FW_RAISE_IF(error, !result.first);
304 
305  // File the dictionary Structure
306  ::fwData::StructureTraitsDictionary::sptr structDico = getConcreteObject();
307 
308  for(::fwDataIO::line line : dicolines)
309  {
310  ::fwData::StructureTraits::sptr newOrgan = ::fwData::StructureTraits::New();
311  newOrgan->setType(line.type);
312 
313  std::string classReformated = reformatString(line.organClass);
314  ::fwData::StructureTraitsHelper::ClassTranslatorType::right_const_iterator strClassIter =
315  ::fwData::StructureTraitsHelper::s_CLASSTRANSLATOR.right.find(classReformated);
316  std::string availableValues = getValues(::fwData::StructureTraitsHelper::s_CLASSTRANSLATOR.right);
317  error = "Organ class " + classReformated + " isn't available. Authorized type are " + availableValues;
318  FW_RAISE_IF(error, !(strClassIter != ::fwData::StructureTraitsHelper::s_CLASSTRANSLATOR.right.end()));
319  newOrgan->setClass(strClassIter->second);
320 
321  newOrgan->setColor(::fwData::Color::New(line.red/255.0f, line.green/255.0f, line.blue/255.0f,
322  line.alpha/100.0f));
323  std::vector<std::string> categorylist;
324  ::boost::algorithm::split( categorylist, line.catgegory, ::boost::algorithm::is_any_of(",") );
325  ::fwData::StructureTraits::CategoryContainer categories;
326  for(std::string category : categorylist)
327  {
328  std::string catReformated = reformatString(category);
329  ::fwData::StructureTraitsHelper::CategoryTranslatorType::right_const_iterator strCategoryIter =
331  availableValues = getValues(
333  error =
334  "Category " + catReformated + " isn't available. Authorized type are " + availableValues;
335  FW_RAISE_IF(error, !(strCategoryIter != ::fwData::StructureTraitsHelper::s_CATEGORYTRANSLATOR.right.end()));
336  categories.push_back(strCategoryIter->second);
337  }
338  newOrgan->setCategories(categories);
339  newOrgan->setAttachmentType(line.attachment);
340  newOrgan->setNativeExp(line.nativeExp);
341  newOrgan->setNativeGeometricExp(line.nativeExpGeo);
342  newOrgan->setAnatomicRegion(line.anatomicRegion);
343  newOrgan->setPropertyCategory(line.propertyCategory);
344  newOrgan->setPropertyType(line.propertyType);
345  structDico->addStructure(newOrgan);
346  }
347 }
348 
349 //------------------------------------------------------------------------------
350 
351 std::string DictionaryReader::extension()
352 {
353  SLM_TRACE_FUNC();
354  return (".dic");
355 }
356 
357 //------------------------------------------------------------------------------
358 
360 {
361  return ::fwRuntime::getLibraryResourceFilePath(PRJ_NAME "-" FWDATAIO_VER "/OrganDictionary.dic");
362 }
363 
364 //------------------------------------------------------------------------------
365 
366 } // namespace reader
367 } // namespace fwDataIO
static FWDATA_API const ClassTranslatorType s_CLASSTRANSLATOR
Map to translate structure class (string vs enum)
virtual FWDATAIO_API ~DictionaryReader()
Destructor. Do nothing.
#define SLM_TRACE_FUNC()
Trace contextual function signature.
Definition: spyLog.hpp:329
This class defines a single file location.
Definition: SingleFile.hpp:25
This namespace fwDataIO contains reader and writer for several framework&#39;s data.
Key class used to restrict access to Object construction. See http://www.drdobbs.com/184402053.
#define OSLM_INFO(message)
Definition: spyLog.hpp:252
#define SLM_DEBUG(message)
Definition: spyLog.hpp:239
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
virtual std::shared_ptr< DataType > getConcreteObject()
m_object getter.
Dictionary reader. Read file with .dic extension.
static FWDATA_API const CategoryTranslatorType s_CATEGORYTRANSLATOR
Map to translate structure categories (string vs enum)
virtual FWDATAIO_API void read() override
Read the file with standard iostream API.
Contains the representation of the data objects used in the framework.
::fwData::location::ILocation::sptr m_location
Object location ( file path, directory path, url, etc )
static FWDATAIO_API::boost::filesystem::path getDefaultDictionaryPath()
Returns the default dictionary path ( file in rc directory path of fwDataIO library ) ...