fw4spl
SegmentedPropertyRegistry.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 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 "fwGdcmIO/helper/SegmentedPropertyRegistry.hpp"
8 
9 #include "fwGdcmIO/helper/CsvIO.hpp"
10 #include "fwGdcmIO/helper/DicomCodedAttribute.hpp"
11 
12 #include <fwLog/Logger.hpp>
13 
14 #include <boost/filesystem/operations.hpp>
15 
16 namespace fwGdcmIO
17 {
18 namespace helper
19 {
20 
21 //------------------------------------------------------------------------------
22 
23 const SegmentedPropertyRegistry::EntryType SegmentedPropertyRegistry::s_DEFAULT_ENTRY_VALUE =
24 {{"(111176;DCM;Unspecified)", "(T-D000A;SRT;Anatomical Structure)", "", "", ""}};
25 
26 //------------------------------------------------------------------------------
27 
29 {
30 }
31 
32 //------------------------------------------------------------------------------
33 
34 bool checkAndFormatEntry(const std::string& structureType,
36  const ::fwLog::Logger::sptr& logger)
37 {
38  bool result = true;
39 
40  // Check condition and add critical log on failure
41  auto checkCondition = [&](bool condition, const std::string& msg)
42  {
43  result &= condition;
44 
45  SLM_ERROR_IF(msg, !condition && !logger);
46  if(!condition && logger)
47  {
48  logger->critical(msg);
49  }
50  };
51 
52  // Structure type
53  checkCondition(!structureType.empty(), "Structure type of registry entries shall not be empty.");
54 
55  // Property type
56  std::string& propertyType = entry[0];
57  checkCondition(!propertyType.empty(), "Property Type shall not be empty for '" + structureType + "'.");
58  checkCondition(propertyType.empty() ||
60  "Coded entry is badly formatted : '" + propertyType + "'. Please check registry.");
61 
62  // Property category
63  std::string& propertyCategory = entry[1];
64  checkCondition(!propertyCategory.empty(), "Property Category shall not be empty for '" + structureType + "'.");
65  checkCondition(propertyCategory.empty() ||
67  "Coded entry is badly formatted : '" + propertyCategory + "'. Please check registry.");
68 
69  // Property type modifiers (may be empty)
70  std::string& propertyTypeModifiers = entry[2];
71  checkCondition(propertyTypeModifiers.empty() ||
73  "Coded entry is badly formatted : '" + propertyTypeModifiers + "'. Please check registry.");
74 
75  // Anatomic region
76  std::string& anatomicRegion = entry[3];
77  checkCondition(propertyCategory != "(M-01000;SRT;Morphologically Altered Structure)" || !anatomicRegion.empty(),
78  "Anatomic Region shall not be empty for altered structures. See '" + structureType + "'.");
79  checkCondition(anatomicRegion.empty() ||
81  "Coded entry is badly formatted : '" + anatomicRegion + "'. Please check registry.");
82 
83  // Anatomic region modifiers (may be empty)
84  std::string& anatomicRegionModifiers = entry[4];
85  checkCondition(anatomicRegionModifiers.empty() ||
87  "Coded entry is badly formatted : '" + anatomicRegionModifiers + "'. Please check registry.");
88 
89  return result;
90 }
91 
92 //------------------------------------------------------------------------------
93 
94 bool SegmentedPropertyRegistry::readSegmentedPropertyRegistryFile(const ::boost::filesystem::path& filepath,
95  bool omitFirstLine,
96  const ::fwLog::Logger::sptr& logger)
97 {
98  const std::string filepathStr = filepath.string();
99  if(::boost::filesystem::exists(filepath))
100  {
101  std::ifstream csvStream(filepathStr);
102  return readSegmentedPropertyRegistryFile(csvStream, omitFirstLine, logger);
103  }
104  return false;
105 }
106 
107 //------------------------------------------------------------------------------
108 
110  bool omitFirstLine,
111  const ::fwLog::Logger::sptr& logger)
112 {
113  bool result = true;
114 
115  // Read CSV
116  ::fwGdcmIO::helper::CsvIO reader(csvStream);
117 
118  const std::string separator = "|";
119  auto tokens = reader.getLine(separator);
120 
121  if(omitFirstLine)
122  {
123  // First line is omitted (titles)
124  tokens = reader.getLine(separator);
125  }
126 
127  while(!tokens.empty())
128  {
129  const std::string structureType = tokens[0];
130 
131  if(tokens.size() >= 6)
132  {
133  EntryType entry;
134  std::copy_n(tokens.begin()+1, 5, entry.begin());
135  if(checkAndFormatEntry(structureType, entry, logger))
136  {
137  m_registry[structureType] = entry;
138  }
139  else
140  {
141  result = false;
142  }
143  }
144  else if(logger)
145  {
146  logger->critical("Entry badly formatted for structure '" + structureType + "'.");
147  result = false;
148  }
149 
150  tokens = reader.getLine(separator);
151  }
152 
153  // Check uniqueness of entries
154  std::set< EntryType > uniquenessSet;
155  for(const auto& entry : m_registry)
156  {
157  if(uniquenessSet.find(entry.second) != uniquenessSet.end())
158  {
159  const std::string msg = "Several structure types have the same attribute combination : {" +
160  entry.second[0] + ";" +
161  entry.second[1] + ";" +
162  entry.second[2] + ";" +
163  entry.second[3] + ";" +
164  entry.second[4] + "}";
165 
166  SLM_ERROR_IF(msg, !logger);
167  if(logger)
168  {
169  logger->critical(msg);
170  }
171 
172  result = false;
173  }
174 
175  uniquenessSet.insert(entry.second);
176 
177  }
178 
179  return result;
180 
181 }
182 
183 //------------------------------------------------------------------------------
184 
186 {
187  return m_registry.empty();
188 }
189 
190 //------------------------------------------------------------------------------
191 
193 {
194  return m_registry.size();
195 }
196 
197 //------------------------------------------------------------------------------
198 
200 {
201  m_registry.clear();
202 }
203 
204 //------------------------------------------------------------------------------
205 
206 bool SegmentedPropertyRegistry::hasEntry(const std::string& structureType) const
207 {
208  return m_registry.find(structureType) != m_registry.end();
209 }
210 
211 //------------------------------------------------------------------------------
212 
214 {
215  const auto it = m_registry.find(structureType);
216  if(it != m_registry.end())
217  {
218  return it->second;
219  }
220  return s_DEFAULT_ENTRY_VALUE;
221 }
222 
223 //------------------------------------------------------------------------------
224 
225 #define GET_ENTRY_VALUE(index) \
226  const auto it = m_registry.find(structureType); \
227  if(it != m_registry.end()) \
228  { \
229  return it->second[index]; \
230  } \
231  return s_DEFAULT_ENTRY_VALUE[index];
232 
233 //------------------------------------------------------------------------------
234 
235 std::string SegmentedPropertyRegistry::getPropertyType(const std::string& structureType) const
236 {
237  GET_ENTRY_VALUE(0);
238 }
239 
240 //------------------------------------------------------------------------------
241 
242 std::string SegmentedPropertyRegistry::getPropertyCategory(const std::string& structureType) const
243 {
244  GET_ENTRY_VALUE(1);
245 }
246 
247 //------------------------------------------------------------------------------
248 
249 std::string SegmentedPropertyRegistry::getPropertyTypeModifiers(const std::string& structureType) const
250 {
251  GET_ENTRY_VALUE(2);
252 }
253 
254 //------------------------------------------------------------------------------
255 
256 std::string SegmentedPropertyRegistry::getAnatomicRegion(const std::string& structureType) const
257 {
258  GET_ENTRY_VALUE(3);
259 }
260 
261 //------------------------------------------------------------------------------
262 
263 std::string SegmentedPropertyRegistry::getAnatomicRegionModifiers(const std::string& structureType) const
264 {
265  GET_ENTRY_VALUE(4);
266 }
267 
268 #undef GET_ENTRY_VALUE
269 
270 //------------------------------------------------------------------------------
271 
272 std::string SegmentedPropertyRegistry::getStructureType(const std::string& propertyType,
273  const std::string& propertyCategory,
274  const std::string& propertyTypeModifiers,
275  const std::string& anatomicRegion,
276  const std::string& anatomicRegionModifiers) const
277 {
278  // Entry that we are looking for
279  const EntryType entry = {{ propertyType,
280  propertyCategory,
281  propertyTypeModifiers,
282  anatomicRegion,
283  anatomicRegionModifiers }};
284 
285  // Search predicate
286  const auto predicate = [&](const EntryRegistryType::value_type& value) -> bool
287  {
288  return value.second == entry;
289  };
290 
291  // Search for entry
292  const auto it = std::find_if(m_registry.begin(), m_registry.end(), predicate);
293 
294  return (it != m_registry.end()) ? it->first : "";
295 }
296 
297 //------------------------------------------------------------------------------
298 
299 } //namespace helper
300 } //namespace fwGdcmIO
301 
FWGDCMIO_API std::string getPropertyCategory(const std::string &structureType) const
Getters for entry&#39;s attributes.
FWGDCMIO_API TokenContainerType getLine(const std::string &separator=",")
Returns tokens on next line to read, using comma separator.
Definition: CsvIO.cpp:42
FWGDCMIO_API std::string getPropertyType(const std::string &structureType) const
Getters for entry&#39;s attributes.
The namespace fwGdcmIO contains reader, writer and helper for dicom data.
FWGDCMIO_API EntryType getEntry(const std::string &structureType) const
Returns matching entry for the corresponding structure type.
FWGDCMIO_API std::string getPropertyTypeModifiers(const std::string &structureType) const
Getters for entry&#39;s attributes.
FWGDCMIO_API std::string getAnatomicRegion(const std::string &structureType) const
Getters for entry&#39;s attributes.
static FWGDCMIO_API bool checkAndFormatEntry(std::string &entry, bool multipleValue=false)
Retrieve &#39;(AAA;BBB;CCC)&#39; patterns inside of the entry string. Remove spaces or other characters that ...
FWGDCMIO_API bool empty() const
Returns whether the registry is empty or not.
#define SLM_ERROR_IF(message, cond)
Definition: spyLog.hpp:276
FWGDCMIO_API std::string getStructureType(const std::string &propertyType, const std::string &propertyCategory, const std::string &propertyTypeModifiers, const std::string &anatomicRegion, const std::string &anatomicRegionModifiers) const
Returns the structure type associated to the attribute list. If no match is found, it returns an empty string.
FWGDCMIO_API std::string getAnatomicRegionModifiers(const std::string &structureType) const
Getters for entry&#39;s attributes.
FWGDCMIO_API void clear()
Clear the registry.
FWGDCMIO_API bool readSegmentedPropertyRegistryFile(const ::boost::filesystem::path &filepath, bool omitFirstLine=false, const std::shared_ptr< ::fwLog::Logger > &logger=0)
Read an extract registry values from a CSV file Each lines shall contain at least 6 elements : ...
Read CSV file and returns parsed tokens. The input file is supposed to use comma separator, but another separator can be used when reading file.
Definition: CsvIO.hpp:26
FWGDCMIO_API std::size_t count() const
Returns the number of entries.
std::array< std::string, 5 > EntryType
Entry containing the 5 attributes of a structure type.
FWGDCMIO_API bool hasEntry(const std::string &structureType) const
Check if there is an entry for the corresponding structure type.