Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkDICOMTagPath.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 #include <algorithm>
18 #include <utility>
19 
20 #include <mitkDICOMTagPath.h>
21 #include <mitkExceptionMacro.h>
22 
23 #include <regex>
24 
25 namespace
26 {
27  std::string GenerateRegExForNumber(unsigned int tagNumber)
28  {
29  std::ostringstream resultRegEx;
30 
31  std::ostringstream hexNumber;
32  hexNumber << std::hex << tagNumber; // default std::hex output is lowercase
33  std::regex reg_character("([a-f]+)"); // so only check for lowercase characters here
34  if (std::regex_search(hexNumber.str(), reg_character))
35  {
36  // hexNumber contains a characters from a-f
37  // needs to be generated with lowercase and uppercase characters
38  resultRegEx << "("
39  << std::setw(4) << std::setfill('0') << std::hex << tagNumber
40  << "|"
41  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << tagNumber << std::nouppercase
42  << ")";
43  }
44  else
45  {
46  // only decimal values (0-9) contained in hexNumber - no need to modify the result
47  resultRegEx << std::setw(4) << std::setfill('0') << std::hex << tagNumber;
48  }
49 
50  return resultRegEx.str();
51  }
52 }
53 
54 namespace mitk
55 {
56 
58  NodeInfo() : type(NodeType::Invalid), tag(0, 0), selection(0)
59  {
60  ;
61  };
62 
64  NodeInfo(const DICOMTag& aTag, NodeType aType, ItemSelectionIndex index) : type(aType), tag(aTag), selection(index)
65  {};
66 
68  {
69  if (!(this->tag == right.tag)) return false;
70  if (this->type != right.type) return false;
71  if (this->selection != right.selection) return false;
72 
73  return true;
74  };
75 
77  Matches(const NodeInfo& right) const
78  {
79  if (type == NodeType::AnyElement || right.type == NodeType::AnyElement)
80  {
81  return true;
82  }
83  else if (tag == right.tag && type != NodeType::Invalid && right.type != NodeType::Invalid)
84  {
85  if (type == NodeType::Element && right.type == NodeType::Element)
86  {
87  return true;
88  }
89  else if(selection == right.selection || type == NodeType::AnySelection || right.type == NodeType::AnySelection)
90  {
91  return true;
92  }
93  }
94  return false;
95  };
96 
97  bool DICOMTagPath::IsEmpty() const
98  {
99  return m_NodeInfos.empty();
100  };
101 
102  bool
104  IsExplicit() const
105  {
106  for (const auto & pos : m_NodeInfos)
107  {
108  if ((pos.type == NodeInfo::NodeType::AnySelection) || (pos.type == NodeInfo::NodeType::AnyElement)) return false;
109  }
110 
111  return true;
112  };
113 
114  bool
117  {
118  bool result = false;
119  for (const auto & pos : m_NodeInfos)
120  {
121  if (pos.type == NodeInfo::NodeType::AnyElement) return false;
122  result = result || pos.type == NodeInfo::NodeType::AnySelection;
123  }
124 
125  return result;
126  };
127 
129  {
130  return m_NodeInfos.size();
131  }
132 
135  AddNode(const NodeInfo& newNode)
136  {
137  m_NodeInfos.push_back(newNode);
138  return m_NodeInfos.size() - 1;
139  };
140 
143  GetNode(const PathIndexType& index) const
144  {
145  if (index >= Size())
146  {
147  mitkThrow() << "Error. Cannot return info of path node. Node index is out of bound. Index: " << index << "; Path: " << this->ToStr();
148  }
149 
150  return m_NodeInfos[index];
151  };
152 
155  GetNode(const PathIndexType& index)
156  {
157  if (index >= Size())
158  {
159  mitkThrow() << "Error. Cannot return info of path node. Node index is out of bound. Index: " << index << "; Path: " << this->ToStr();
160  }
161 
162  return m_NodeInfos[index];
163  };
164 
168  {
169  return GetNode(0);
170  };
171 
175  {
176  return GetNode(Size() - 1);
177  };
178 
182  {
183  return GetNode(Size() - 1);
184  };
185 
188  GetNodes() const
189  {
190  return m_NodeInfos;
191  };
192 
193  std::string
195  ToStr() const
196  {
197  std::ostringstream nameStream;
198 
199  if (this->Size() == 0) return nameStream.str();
200 
201  PathIndexType i = 0;
202  for (const auto& node : m_NodeInfos)
203  {
204  if (i)
205  {
206  nameStream << ".";
207  }
208  ++i;
209 
210  if (node.type == NodeInfo::NodeType::AnyElement)
211  {
212  nameStream << "*";
213  }
214  else if (node.type != NodeInfo::NodeType::Invalid)
215  {
216  nameStream << "("
217  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase
218  << ","
219  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement() << std::nouppercase
220  << ")";
221 
222  if (node.type == NodeInfo::NodeType::SequenceSelection)
223  {
224  nameStream << "[" << node.selection << "]";
225  }
226  else if (node.type == NodeInfo::NodeType::AnySelection)
227  {
228  nameStream << "[*]";
229  }
230  }
231  else
232  {
233  nameStream << "INVALID_NODE";
234  }
235  }
236 
237  return nameStream.str();
238  };
239 
240  bool
242  operator == (const DICOMTagPath& path) const
243  {
244  return this->m_NodeInfos == path.m_NodeInfos;
245  };
246 
247  bool
249  operator < (const DICOMTagPath& right) const
250  {
251  auto rightIter = right.m_NodeInfos.cbegin();
252  const auto rightEnd = right.m_NodeInfos.cend();
253  for (const auto& leftPos : m_NodeInfos)
254  {
255  if (rightIter == rightEnd) return false;
256 
257  if (leftPos.tag.GetElement() < rightIter->tag.GetElement()) return true;
258  if (rightIter->tag.GetElement() < leftPos.tag.GetElement()) return false;
259 
260  if (leftPos.tag.GetGroup() < rightIter->tag.GetGroup()) return true;
261  if (rightIter->tag.GetGroup()< leftPos.tag.GetGroup()) return false;
262 
263  if (leftPos.type < rightIter->type) return true;
264  if (rightIter->type< leftPos.type) return false;
265 
266  if (leftPos.selection < rightIter->selection) return true;
267  if (rightIter->selection< leftPos.selection) return false;
268  ++rightIter;
269  }
270  return rightIter != rightEnd;
271  }
272 
273  bool
275  Equals(const DICOMTagPath& path) const
276  {
277  return DICOMTagPathesMatch(*this, path);
278  };
279 
280  DICOMTagPath&
283  {
284  if (&path == this) return *this;
285 
286  this->m_NodeInfos = path.m_NodeInfos;
287 
288  return *this;
289  };
290 
291  DICOMTagPath&
293  {
295  return *this;
296  };
297 
298  DICOMTagPath&
299  DICOMTagPath::AddElement(unsigned int group, unsigned int element)
300  {
301  m_NodeInfos.emplace_back(DICOMTag(group, element), NodeInfo::NodeType::Element);
302  return *this;
303  };
304 
305  DICOMTagPath&
306  DICOMTagPath::AddAnySelection(unsigned int group, unsigned int element)
307  {
308  m_NodeInfos.emplace_back(DICOMTag(group, element), NodeInfo::NodeType::AnySelection);
309  return *this;
310  };
311 
312  DICOMTagPath&
313  DICOMTagPath::AddSelection(unsigned int group, unsigned int element, ItemSelectionIndex index)
314  {
315  m_NodeInfos.emplace_back(DICOMTag(group, element), NodeInfo::NodeType::SequenceSelection, index);
316  return *this;
317  };
318 
319  DICOMTagPath&
321  FromStr(const std::string& pathStr)
322  {
323  NodeInfoVectorType result;
324  std::istringstream f(pathStr);
325  std::string subStr;
326 
327  while (getline(f, subStr, '.'))
328  {
329  NodeInfo info;
330 
331  if (subStr == "*")
332  {
334  }
335  else
336  {
337  std::regex reg_element("\\(([A - Fa - f\\d]{4}),([A - Fa - f\\d]{4})\\)");
338  std::regex reg_anySelection("\\(([A - Fa - f\\d]{4}),([A - Fa - f\\d]{4})\\)\\[\\*\\]");
339  std::regex reg_Selection("\\(([A - Fa - f\\d]{4}),([A - Fa - f\\d]{4})\\)\\[(\\d+)\\]");
340  std::smatch sm;
341  if (std::regex_match(subStr, sm, reg_anySelection))
342  {
344  info.tag = DICOMTag(std::stoul(sm[1], nullptr, 16), std::stoul(sm[2], nullptr, 16));
345  }
346  else if (std::regex_match(subStr, sm, reg_Selection))
347  {
349  info.tag = DICOMTag(std::stoul(sm[1], nullptr, 16), std::stoul(sm[2], nullptr, 16));
350  info.selection = std::stoi(sm[3]);
351  }
352  else if (std::regex_match(subStr, sm, reg_element))
353  {
355  info.tag = DICOMTag(std::stoul(sm[1], nullptr, 16), std::stoul(sm[2], nullptr, 16));
356  }
357  }
358  result.push_back(info);
359  }
360 
361  this->m_NodeInfos.swap(result);
362  return *this;
363  };
364 
366  {
367  Reset();
368  };
369 
372  {
373  *this = path;
374  };
375 
378  {
379  m_NodeInfos.emplace_back(tag, NodeInfo::NodeType::Element);
380  };
381 
382  DICOMTagPath::DICOMTagPath(unsigned int group, unsigned int element)
383  {
384  m_NodeInfos.emplace_back(DICOMTag(group,element));
385  };
386 
389 
390  void
393  {
394  this->m_NodeInfos.clear();
395  };
396 
397  bool
400  {
401  NodeInfoVectorType::const_iterator leftPos = left.GetNodes().cbegin();
402  NodeInfoVectorType::const_iterator rightPos = right.GetNodes().cbegin();
403  NodeInfoVectorType::const_iterator leftEnd = left.GetNodes().cend();
404  NodeInfoVectorType::const_iterator rightEnd = right.GetNodes().cend();
405 
406  while (leftPos != leftEnd && rightPos != rightEnd)
407  {
408  if (!leftPos->Matches(*rightPos)) break;
409  ++leftPos;
410  ++rightPos;
411  }
412 
413  if (leftPos == leftEnd && rightPos == rightEnd) return true;
414  else return false;
415  };
416 
417  std::ostream & operator<<(std::ostream &os, const DICOMTagPath &value)
418  {
419  os << value.ToStr();
420  return os;
421  };
422 
423  std::string DICOMTagPathToPropertyRegEx(const DICOMTagPath& tagPath)
424  {
425  std::ostringstream nameStream;
426 
427  nameStream << "DICOM";
428 
429  for (const auto& node : tagPath.GetNodes())
430  {
431  nameStream << "\\.";
432 
434  {
435  nameStream << "([A-Fa-f\\d]{4})\\.([A-Fa-f\\d]{4})";
436  }
437  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
438  {
439  nameStream << GenerateRegExForNumber(node.tag.GetGroup())
440  << "\\."
441  << GenerateRegExForNumber(node.tag.GetElement());
442 
444  {
445  nameStream << "\\.\\[" << node.selection << "\\]";
446  }
448  {
449  nameStream << "\\.\\[(\\d*)\\]";
450  }
451  }
452  else
453  {
454  nameStream << "INVALIDNODE";
455  }
456  }
457 
458  return nameStream.str();
459  };
460 
462  {
463  std::ostringstream nameStream;
464 
465  nameStream << "DICOM";
466 
467  for (const auto& node : tagPath.GetNodes())
468  {
469  nameStream << "_";
470 
472  {
473  nameStream << "([A-Fa-f\\d]{4})_([A-Fa-f\\d]{4})";
474  }
475  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
476  {
477  nameStream << GenerateRegExForNumber(node.tag.GetGroup())
478  << "_"
479  << GenerateRegExForNumber(node.tag.GetElement());
480 
482  {
483  nameStream << "_\\[" << node.selection << "\\]";
484  }
486  {
487  nameStream << "_\\[(\\d*)\\]";
488  }
489  }
490  else
491  {
492  nameStream << "INVALIDNODE";
493  }
494  }
495 
496  return nameStream.str();
497  };
498 
500  {
501  std::ostringstream nameStream;
502 
503  nameStream << "DICOM";
504 
505  int captureGroup = 1;
506 
507  for (const auto& node : tagPath.GetNodes())
508  {
509  nameStream << "_";
510 
512  {
513  nameStream << "$" << captureGroup++;
514  nameStream << "_$" << captureGroup++;
515  }
516  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
517  {
518  nameStream << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase << "_"
519  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement();
520 
522  {
523  nameStream << "_[" << node.selection << "]";
524  }
526  {
527  nameStream << "_[$" << captureGroup++ << "]";
528  }
529  }
530  else
531  {
532  nameStream << "INVALID_NODE";
533  }
534  }
535 
536  return nameStream.str();
537  };
538 
540  {
541  std::ostringstream nameStream;
542 
543  nameStream << "DICOM";
544 
545  int captureGroup = 1;
546 
547  for (const auto& node : tagPath.GetNodes())
548  {
549  nameStream << ".";
550 
552  {
553  nameStream << "$" << captureGroup++;
554  nameStream << ".$" << captureGroup++;
555  }
556  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
557  {
558  nameStream << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase << "."
559  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement();
560 
562  {
563  nameStream << ".[" << node.selection << "]";
564  }
566  {
567  nameStream << ".[$"<<captureGroup++<<"]";
568  }
569  }
570  else
571  {
572  nameStream << "INVALID_NODE";
573  }
574  }
575 
576  return nameStream.str();
577  };
578 
579 
580  std::string DICOMTagPathToDCMTKSearchPath(const DICOMTagPath& tagPath)
581  {
582  if (!tagPath.IsExplicit() && !tagPath.HasItemSelectionWildcardsOnly())
583  {
584  mitkThrow() << "Cannot convert DICOMTagPath into DCMTK search path. Path has element wild cards. Path: " << tagPath.ToStr();
585  }
586 
587  return tagPath.ToStr();
588  };
589 
590 
592  PropertyNameToDICOMTagPath(const std::string& propertyName)
593  {
594  DICOMTagPath result;
595 
596  std::istringstream f(propertyName);
597  std::string subStr;
598 
599  if (getline(f, subStr, '.'))
600  {
601  if (subStr != "DICOM")
602  {
603  return DICOMTagPath();
604  }
605 
606  unsigned int nrCount = 0;
607  unsigned long group = 0;
608  unsigned long element = 0;
609 
610  while (getline(f, subStr, '.'))
611  {
612  if (subStr == "*")
613  {
614  if (nrCount == 2)
615  { //add last element
616  result.AddElement(group, element);
617  nrCount = 0;
618  }
619  else if (nrCount != 0)
620  { //invalid path
621  return DICOMTagPath();
622  }
623 
624  result.AddAnyElement();
625  }
626  else
627  {
628  std::regex reg_element("([A-Fa-f\\d]{4})");
629  std::regex reg_anySelection("\\[\\*\\]");
630  std::regex reg_Selection("\\[(\\d+)\\]");
631  std::smatch sm;
632  if (std::regex_match(subStr, sm, reg_anySelection))
633  {
634  if (nrCount == 2)
635  {
636  result.AddAnySelection(group, element);
637  nrCount = 0;
638  }
639  else
640  { //invalid path
641  return DICOMTagPath();
642  }
643  }
644  else if (std::regex_match(subStr, sm, reg_Selection))
645  {
646  if (nrCount == 2)
647  {
648  result.AddSelection(group, element, std::stoi(sm[1]));
649  nrCount = 0;
650  }
651  else
652  { //invalid path
653  return DICOMTagPath();
654  }
655  }
656  else if (std::regex_match(subStr, sm, reg_element))
657  {
658  if (nrCount == 0)
659  {
660  group = std::stoul(sm[1], nullptr, 16);
661  ++nrCount;
662  }
663  else if (nrCount == 1)
664  {
665  element = std::stoul(sm[1], nullptr, 16);
666  ++nrCount;
667  }
668  else if (nrCount == 2)
669  { //store the last element and start the next
670  result.AddElement(group, element);
671  group = std::stoul(sm[1], nullptr, 16);
672  nrCount = 1;
673  }
674  else
675  { //invalid path
676  return DICOMTagPath();
677  }
678  }
679  }
680  }
681 
682  if (nrCount == 2)
683  { //add last element
684  result.AddElement(group, element);
685  nrCount = 0;
686  }
687  }
688 
689  return result;
690  };
691 
692  std::string
694  {
695  std::ostringstream nameStream;
696 
697  nameStream << "DICOM";
698 
699  for (const auto& node : tagPath.GetNodes())
700  {
701  nameStream << ".";
702 
704  {
705  nameStream << "*";
706  }
707  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
708  {
709  nameStream << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase << "."
710  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement();
711 
713  {
714  nameStream << ".[" << node.selection << "]";
715  }
717  {
718  nameStream << ".[*]";
719  }
720  }
721  else
722  {
723  nameStream << "INVALID_NODE";
724  }
725  }
726 
727  return nameStream.str();
728  };
729 }
DICOMTagPath & AddElement(unsigned int group, unsigned int element)
DICOMTagPath & AddAnySelection(unsigned int group, unsigned int element)
bool operator==(const DICOMTagPath &path) const
Class is used to identify (nested) attributes in a DICOM dataset. In contrast to the class DICOMTag...
MITKCORE_EXPORT std::ostream & operator<<(std::ostream &o, DataNode::Pointer &dtn)
bool Equals(const DICOMTagPath &path) const
NodeInfoVectorType m_NodeInfos
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPersistenceKeyRegEx(const DICOMTagPath &tagPath)
DICOMTagPath & AddAnyElement()
Representation of a DICOM tag.
Definition: mitkDICOMTag.h:37
bool operator==(const NodeInfo &right) const
std::vector< NodeInfo > NodeInfoVectorType
DataCollection - Class to facilitate loading/accessing structured data.
std::string ToStr() const
PathIndexType AddNode(const NodeInfo &newNode)
static void info(const char *fmt,...)
Definition: svm.cpp:100
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPersistenceNameTemplate(const DICOMTagPath &tagPath)
const NodeInfoVectorType & GetNodes() const
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPropertyName(const DICOMTagPath &tagPath)
bool IsExplicit() const
static bool DICOMTagPathesMatch(const DICOMTagPath &left, const DICOMTagPath &right)
NodeInfoVectorType::size_type PathIndexType
PathIndexType Size() const
DICOMTagPath & FromStr(const std::string &pathStr)
#define mitkThrow()
const NodeInfo & GetNode(const PathIndexType &index) const
bool operator<(const DICOMTagPath &right) const
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPersistenceKeyTemplate(const DICOMTagPath &tagPath)
DICOMTagPath & operator=(const DICOMTagPath &path)
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPropertyRegEx(const DICOMTagPath &tagPath)
bool HasItemSelectionWildcardsOnly() const
DICOMTagPath & AddSelection(unsigned int group, unsigned int element, ItemSelectionIndex index)
bool Matches(const NodeInfo &right) const
ItemSelectionIndex selection
MITKDICOMREADER_EXPORT std::string DICOMTagPathToDCMTKSearchPath(const DICOMTagPath &tagPath)
MITKDICOMREADER_EXPORT DICOMTagPath PropertyNameToDICOMTagPath(const std::string &propertyName)
NodeInfo & GetFirstNode()