1 /*=========================================================================
2 
3   Program: GDCM (Grassroots DICOM). A DICOM library
4 
5   Copyright (c) 2006-2011 Mathieu Malaterre
6   All rights reserved.
7   See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
8 
9      This software is distributed WITHOUT ANY WARRANTY; without even
10      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11      PURPOSE.  See the above copyright notice for more information.
12 
13 =========================================================================*/
14 #ifndef GDCMDICT_H
15 #define GDCMDICT_H
16 
17 #include "gdcmTypes.h"
18 #include "gdcmTag.h"
19 #include "gdcmPrivateTag.h"
20 #include "gdcmDictEntry.h"
21 #include "gdcmSystem.h"
22 
23 #include <iostream>
24 #include <iomanip>
25 #include <map>
26 
27 /*
28  * FIXME / TODO
29  * I need to seriously rewrite this mess. a class template should work for both a public
30  * and a private dict
31  */
32 
33 namespace gdcm
34 {
35 // Data Element Tag
36 /**
37  * \brief Class to represent a map of DictEntry
38  * \note bla
39  * TODO FIXME: For Element == 0x0 need to return
40  * Name = Group Length
41  * ValueRepresentation = UL
42  * ValueMultiplicity = 1
43  */
44 class GDCM_EXPORT Dict
45 {
46 public:
47   typedef std::map<Tag, DictEntry> MapDictEntry;
48   typedef MapDictEntry::iterator Iterator;
49   typedef MapDictEntry::const_iterator ConstIterator;
50   //static DictEntry GroupLengthDictEntry; // = DictEntry("Group Length",VR::UL,VM::VM1);
51 
Dict()52   Dict():DictInternal() {
53     assert( DictInternal.empty() );
54   }
55   Dict &operator=(const Dict &_val) = delete;
56   Dict(const Dict &_val) = delete;
57 
58 
59   friend std::ostream& operator<<(std::ostream& _os, const Dict &_val);
60 
Begin()61   ConstIterator Begin() const { return DictInternal.begin(); }
End()62   ConstIterator End() const { return DictInternal.end(); }
63 
IsEmpty()64   bool IsEmpty() const { return DictInternal.empty(); }
AddDictEntry(const Tag & tag,const DictEntry & de)65   void AddDictEntry(const Tag &tag, const DictEntry &de)
66     {
67 #ifndef NDEBUG
68     MapDictEntry::size_type s = DictInternal.size();
69 #endif
70     DictInternal.insert(
71       MapDictEntry::value_type(tag, de));
72     assert( s < DictInternal.size() );
73     }
74 
GetDictEntry(const Tag & tag)75   const DictEntry &GetDictEntry(const Tag &tag) const
76     {
77     MapDictEntry::const_iterator it =
78       DictInternal.find(tag);
79     if (it == DictInternal.end())
80       {
81 #ifdef UNKNOWNPUBLICTAG
82       // test.acr
83       if( tag != Tag(0x28,0x15)
84         && tag != Tag(0x28,0x16)
85         && tag != Tag(0x28,0x199)
86         // gdcmData/TheralysGDCM1.dcm
87         && tag != Tag(0x20,0x1)
88         // gdcmData/0019004_Baseline_IMG1.dcm
89         && tag != Tag(0x8348,0x339)
90         && tag != Tag(0xb5e8,0x338)
91         // gdcmData/dicomdir_Acusson_WithPrivate_WithSR
92         && tag != Tag(0x40,0xa125)
93       )
94         {
95         assert( 0 && "Impossible" );
96         }
97 #endif
98       it = DictInternal.find( Tag(0xffff,0xffff) );
99       return it->second;
100       }
101     assert( DictInternal.count(tag) == 1 );
102     return it->second;
103     }
104 
105   /// Function to return the Keyword from a Tag
GetKeywordFromTag(Tag const & tag)106   const char *GetKeywordFromTag(Tag const & tag) const
107     {
108     MapDictEntry::const_iterator it =
109       DictInternal.find(tag);
110     if (it == DictInternal.end())
111       {
112       return nullptr;
113       }
114     assert( DictInternal.count(tag) == 1 );
115     return it->second.GetKeyword();
116     }
117 
118   /// Lookup DictEntry by keyword. Even if DICOM standard defines keyword
119   /// as being unique. The lookup table is built on Tag. Therefore
120   /// looking up a DictEntry by Keyword is more inefficient than looking up
121   /// by Tag.
GetDictEntryByKeyword(const char * keyword,Tag & tag)122   const DictEntry &GetDictEntryByKeyword(const char *keyword, Tag & tag) const
123     {
124     MapDictEntry::const_iterator it =
125       DictInternal.begin();
126     if( keyword )
127       {
128       for(; it != DictInternal.end(); ++it)
129         {
130         if( strcmp( keyword, it->second.GetKeyword() ) == 0 )
131           {
132           // Found a match !
133           tag = it->first;
134           break;
135           }
136         }
137       }
138     else
139       {
140       it = DictInternal.end();
141       }
142     if (it == DictInternal.end())
143       {
144       tag = Tag(0xffff,0xffff);
145       it = DictInternal.find( tag );
146       return it->second;
147       }
148     assert( DictInternal.count(tag) == 1 );
149     return it->second;
150     }
151 
152   /// Inefficient way of looking up tag by name. Technically DICOM
153   /// does not guarantee uniqueness (and Curve / Overlay are there to prove it).
154   /// But most of the time name is in fact uniq and can be uniquely link to a tag
GetDictEntryByName(const char * name,Tag & tag)155   const DictEntry &GetDictEntryByName(const char *name, Tag & tag) const
156     {
157     MapDictEntry::const_iterator it =
158       DictInternal.begin();
159     if( name )
160       {
161       for(; it != DictInternal.end(); ++it)
162         {
163         if( strcmp( name, it->second.GetName() ) == 0 )
164           {
165           // Found a match !
166           tag = it->first;
167           break;
168           }
169         }
170       }
171     else
172       {
173       it = DictInternal.end();
174       }
175     if (it == DictInternal.end())
176       {
177       tag = Tag(0xffff,0xffff);
178       it = DictInternal.find( tag );
179       return it->second;
180       }
181     assert( DictInternal.count(tag) == 1 );
182     return it->second;
183     }
184 
185 protected:
186   friend class Dicts;
187   void LoadDefault();
188 
189 private:
190   MapDictEntry DictInternal;
191 };
192 //-----------------------------------------------------------------------------
193 inline std::ostream& operator<<(std::ostream& os, const Dict &val)
194 {
195   Dict::MapDictEntry::const_iterator it = val.DictInternal.begin();
196   for(;it != val.DictInternal.end(); ++it)
197     {
198     const Tag &t = it->first;
199     const DictEntry &de = it->second;
200     os << t << " " << de << '\n';
201     }
202 
203   return os;
204 }
205 
206 // TODO
207 // For private dict, element < 0x10 should automatically defined:
208 // Name = "Private Creator"
209 // ValueRepresentation = LO
210 // ValueMultiplicity = 1
211 // Owner = ""
212 
213 /**
214  * \brief Private Dict
215  */
216 class GDCM_EXPORT PrivateDict
217 {
218   typedef std::map<PrivateTag, DictEntry> MapDictEntry;
219   friend std::ostream& operator<<(std::ostream& os, const PrivateDict &val);
220 public:
221   PrivateDict() = default;
222   ~PrivateDict() = default;
AddDictEntry(const PrivateTag & tag,const DictEntry & de)223   void AddDictEntry(const PrivateTag &tag, const DictEntry &de)
224     {
225 #ifndef NDEBUG
226     MapDictEntry::size_type s = DictInternal.size();
227 #endif
228     DictInternal.insert(
229       MapDictEntry::value_type(tag, de));
230 // The following code should only be used when manually constructing a Private.xml file by hand
231 // it will get rid of VR::UN duplicate (ie. if a VR != VR::Un can be found)
232 #if defined(NDEBUG) && 0
233     if( s == DictInternal.size() )
234       {
235       MapDictEntry::iterator it =
236         DictInternal.find(tag);
237       assert( it != DictInternal.end() );
238       DictEntry &duplicate = it->second;
239       assert( de.GetVR() == VR::UN || duplicate.GetVR() == VR::UN );
240       assert( de.GetVR() != duplicate.GetVR() );
241       if( duplicate.GetVR() == VR::UN )
242         {
243         assert( de.GetVR() != VR::UN );
244         duplicate.SetVR( de.GetVR() );
245         duplicate.SetVM( de.GetVM() );
246         assert( GetDictEntry(tag).GetVR() != VR::UN );
247         assert( GetDictEntry(tag).GetVR() == de.GetVR() );
248         assert( GetDictEntry(tag).GetVM() == de.GetVM() );
249         }
250       return;
251       }
252 #endif
253     assert( s < DictInternal.size() /*&& std::cout << tag << "," << de << std::endl*/ );
254     }
255   /// Remove entry 'tag'. Return true on success (element was found
256   /// and remove). return false if element was not found.
RemoveDictEntry(const PrivateTag & tag)257   bool RemoveDictEntry(const PrivateTag &tag)
258     {
259     MapDictEntry::size_type s =
260       DictInternal.erase(tag);
261     assert( s == 1 || s == 0 );
262     return s == 1;
263     }
FindDictEntry(const PrivateTag & tag)264   bool FindDictEntry(const PrivateTag &tag) const
265     {
266     MapDictEntry::const_iterator it =
267       DictInternal.find(tag);
268     if (it == DictInternal.end())
269       {
270       return false;
271       }
272     return true;
273     }
GetDictEntry(const PrivateTag & tag)274   const DictEntry &GetDictEntry(const PrivateTag &tag) const
275     {
276     // if 0x10 -> return Private Creator
277     MapDictEntry::const_iterator it =
278       DictInternal.find(tag);
279     if (it == DictInternal.end())
280       {
281       //assert( 0 && "Impossible" );
282       it = DictInternal.find( PrivateTag(0xffff,0xffff,"GDCM Private Sentinel" ) );
283       assert (it != DictInternal.end());
284       return it->second;
285       }
286     assert( DictInternal.count(tag) == 1 );
287     return it->second;
288     }
289 
290 
PrintXML()291   void PrintXML() const
292     {
293     MapDictEntry::const_iterator it = DictInternal.begin();
294     std::cout << "<dict edition=\"2008\">\n";
295     for(;it != DictInternal.end(); ++it)
296       {
297       const PrivateTag &t = it->first;
298       const DictEntry &de = it->second;
299       std::cout << "  <entry group=\"" << std::hex << std::setw(4)
300         << std::setfill('0') << t.GetGroup() << "\"" <<
301         " element=\"xx" << std::setw(2) << std::setfill('0')<< t.GetElement() << "\"" << " vr=\""
302         << de.GetVR() << "\" vm=\"" << de.GetVM() << "\" owner=\""
303         << t.GetOwner();
304       const char *name = de.GetName();
305       if( *name == 0 )
306         {
307         std::cout << "\"/>\n";
308         }
309       else
310         {
311         std::cout << "\" name=\"" << de.GetName() << "\"/>\n";
312         }
313       }
314     std::cout << "</dict>\n";
315     }
316 
IsEmpty()317   bool IsEmpty() const { return DictInternal.empty(); }
318 protected:
319   friend class Dicts;
320   void LoadDefault();
321 
322 private:
323   PrivateDict &operator=(const PrivateDict &_val) = delete;
324   PrivateDict(const PrivateDict &_val) = delete;
325 
326   MapDictEntry DictInternal;
327 };
328 //-----------------------------------------------------------------------------
329 inline std::ostream& operator<<(std::ostream& os, const PrivateDict &val)
330 {
331   PrivateDict::MapDictEntry::const_iterator it = val.DictInternal.begin();
332   for(;it != val.DictInternal.end(); ++it)
333     {
334     const PrivateTag &t = it->first;
335     const DictEntry &de = it->second;
336     os << t << " " << de << '\n';
337     }
338 
339   return os;
340 }
341 
342 } // end namespace gdcm
343 
344 #endif //GDCMDICT_H
345