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