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 #include "gdcmScanner.h"
15 #include "gdcmReader.h"
16 #include "gdcmGlobal.h"
17 #include "gdcmDicts.h"
18 #include "gdcmDict.h"
19 #include "gdcmDictEntry.h"
20 #include "gdcmStringFilter.h"
21 #include "gdcmProgressEvent.h"
22 #include "gdcmFileNameEvent.h"
23 
24 #include <algorithm> // std::find
25 
26 namespace gdcm
27 {
28 
29 
30 Scanner::~Scanner()
31 = default;
32 
ClearTags()33 void Scanner::ClearTags()
34 {
35   Tags.clear();
36 }
37 
ClearSkipTags()38 void Scanner::ClearSkipTags()
39 {
40   SkipTags.clear();
41 }
42 
AddSkipTag(Tag const & t)43 void Scanner::AddSkipTag( Tag const & t )
44 {
45   SkipTags.insert( t );
46   assert(0); // This is NOT implemented for now
47 }
48 
49 // Warning: API is passing a public tag (no way to specify private tag)
AddPrivateTag(PrivateTag const & t)50 void Scanner::AddPrivateTag( PrivateTag const & t )
51 {
52   static const Global &g = GlobalInstance;
53   static const Dicts &dicts = g.GetDicts();
54   const DictEntry &entry = dicts.GetDictEntry( t );
55   //std::cout << "Debug: " << entry << std::endl;
56   // Is this tag an ASCII on ?
57   if( entry.GetVR() & VR::VRASCII )
58     {
59     PrivateTags.insert( t );
60     }
61   else if( entry.GetVR() == VR::INVALID )
62     {
63     gdcmWarningMacro( "Only tag with known VR are allowed. Tag " << t << " will be discarded" );
64     }
65   else
66     {
67     assert( entry.GetVR() & VR::VRBINARY );
68     //gdcmWarningMacro( "Only ASCII VR are supported for now. Tag " << t << " will be discarded" );
69     PrivateTags.insert( t );
70     }
71 }
72 
AddTag(Tag const & t)73 void Scanner::AddTag( Tag const & t )
74 {
75   static const Global &g = GlobalInstance;
76   static const Dicts &dicts = g.GetDicts();
77   const DictEntry &entry = dicts.GetDictEntry( t );
78   // Is this tag an ASCII on ?
79   if( entry.GetVR() & VR::VRASCII )
80     {
81     Tags.insert( t );
82     }
83   else if( entry.GetVR() == VR::INVALID )
84     {
85     gdcmWarningMacro( "Only tag with known VR are allowed. Tag " << t << " will be discarded" );
86     }
87   else
88     {
89     assert( entry.GetVR() & VR::VRBINARY );
90     //gdcmWarningMacro( "Only ASCII VR are supported for now. Tag " << t << " will be discarded" );
91     Tags.insert( t );
92     }
93 }
94 
Scan(Directory::FilenamesType const & filenames)95 bool Scanner::Scan( Directory::FilenamesType const & filenames )
96 {
97   this->InvokeEvent( StartEvent() );
98 
99   // Is there at least one tag ?
100   if( !Tags.empty() || !PrivateTags.empty() )
101     {
102     //if( filenames.empty() ) return true;
103 
104     // Prepare hash table:
105     Mappings.clear();
106     Mappings[""]; // Create a fake table for dummy file
107 
108     // Make our own copy:
109     Filenames = filenames;
110 
111     // Find the tag with the highest value (get the one from the end of the std::set)
112     Tag last;
113     if( !Tags.empty() )
114       {
115       TagsType::const_reverse_iterator it1 = Tags.rbegin();
116       const Tag & publiclast = *it1;
117       last = publiclast;
118       }
119     if( !PrivateTags.empty() )
120       {
121       PrivateTagsType::const_reverse_iterator pit1 = PrivateTags.rbegin();
122       Tag privatelast = *pit1;
123       if( last < privatelast ) last = privatelast;
124       }
125 
126     StringFilter sf;
127     Directory::FilenamesType::const_iterator it = Filenames.begin();
128     const double progresstick = 1. / (double)Filenames.size();
129     Progress = 0;
130     for(; it != Filenames.end(); ++it)
131       {
132       Reader reader;
133       const char *filename = it->c_str();
134       assert( filename );
135       reader.SetFileName( filename );
136       bool read = false;
137       try
138         {
139         // Start reading all tags, including the 'last' one:
140         read = reader.ReadUpToTag(last, SkipTags);
141         }
142       catch(std::exception & ex)
143         {
144         (void)ex;
145         gdcmWarningMacro( "Failed to read:" << filename << " with ex:" << ex.what() );
146         }
147       catch(...)
148         {
149         gdcmWarningMacro( "Failed to read:" << filename  << " with unknown error" );
150         }
151       if( read )
152         {
153         // Keep the mapping:
154         sf.SetFile( reader.GetFile() );
155         Scanner::ProcessPublicTag(sf, filename);
156         //Scanner::ProcessPrivateTag(sf, filename);
157         }
158       // Update progress
159       Progress += progresstick;
160       ProgressEvent pe;
161       pe.SetProgress( Progress );
162       this->InvokeEvent( pe );
163       // For outside application tell which file is being processed:
164       FileNameEvent fe( filename );
165       this->InvokeEvent( fe );
166       }
167     }
168 
169   this->InvokeEvent( EndEvent() );
170   return true;
171 }
172 
Print(std::ostream & os) const173 void Scanner::Print( std::ostream & os ) const
174 {
175   os << "Values:\n";
176   for(ValuesType::const_iterator it = Values.begin() ; it != Values.end();
177     ++it)
178     {
179     os << *it << "\n";
180     }
181   os << "Mapping:\n";
182   Directory::FilenamesType::const_iterator file = Filenames.begin();
183   for(; file != Filenames.end(); ++file)
184     {
185     const char *filename = file->c_str();
186     assert( filename && *filename );
187     bool b = IsKey(filename);
188     const char *comment = !b ? "could not be read" : "could be read";
189     os << "Filename: " << filename << " (" << comment << ")\n";
190     //const FilenameToValue &mapping = Mappings[*tag];
191     if( Mappings.find(filename) != Mappings.end() )
192       {
193       const TagToValue &mapping = GetMapping(filename);
194       TagToValue::const_iterator it = mapping.begin();
195       for( ; it != mapping.end(); ++it)
196         {
197         const Tag & tag = it->first;
198         const char *value = it->second;
199         os << tag << " -> [" << value << "]\n";
200         }
201       }
202     }
203 }
204 
IsVRUI(Tag const & tag)205 static bool IsVRUI(Tag const &tag)
206 {
207   static const Global &g = Global::GetInstance();
208   static const Dicts &dicts = g.GetDicts();
209   const DictEntry &dictentry = dicts.GetDictEntry(tag);
210   if( dictentry.GetVR() == VR::UI ) return true;
211   //if( tag == Tag(0x0020,0x000d)   // Study Instance UID : UI
212   // || tag == Tag(0x0020,0x0052)   //
213   // || tag == Tag(0x0020,0x000e) ) // Series Instance UID : UI
214   //  {
215   //  return true;
216   //  }
217   return false;
218 }
219 
PrintTable(std::ostream & os) const220 void Scanner::PrintTable( std::ostream & os ) const
221 {
222   Directory::FilenamesType::const_iterator file = Filenames.begin();
223   for(; file != Filenames.end(); ++file)
224     {
225     const char *filename = file->c_str();
226     assert( filename && *filename );
227     os << '"' << filename << '"' << "\t";
228     TagsType::const_iterator tag = Tags.begin();
229     const TagToValue &mapping = GetMapping(filename);
230     for( ; tag != Tags.end(); ++tag )
231       {
232       const Tag &t = *tag;
233       bool isui = IsVRUI(t);
234       const char *value = "";
235       if( mapping.find(t) != mapping.end() ) {
236         const char * v = mapping.find(t)->second;
237         if(v) value = v;
238       }
239       os << '"' << (isui ? String<>::Trim( value ) : value) << '"';
240       os << "\t";
241       }
242     os << "\n";
243     }
244 }
245 
GetMapping(const char * filename) const246 Scanner::TagToValue const & Scanner::GetMapping(const char *filename) const
247 {
248 //  assert( Mappings.find(filename) != Mappings.end() );
249   assert( filename && *filename );
250   if( Mappings.find(filename) != Mappings.end() )
251     return Mappings.find(filename)->second;
252   return Mappings.find("")->second; // dummy file could not be found
253 }
254 
IsKey(const char * filename) const255 bool Scanner::IsKey( const char * filename ) const
256 {
257 /*
258   // std::find on contiguous array will operate in 0(n) which is way too slow, assume user is not too dumb...
259   Directory::FilenamesType::const_iterator it = std::find(Filenames.begin(), Filenames.end(), filename);
260   if( it == Filenames.end() )
261     {
262     gdcmErrorMacro( "The file: " << filename << " was not scanned" );
263     return false;
264     }
265 */
266   // Look for the file in Mappings table:
267   assert( filename && *filename );
268   MappingType::const_iterator it2 = Mappings.find(filename);
269   return it2 != Mappings.end();
270 }
271 
272 
GetKeys() const273 Directory::FilenamesType Scanner::GetKeys() const
274 {
275   Directory::FilenamesType keys;
276 
277   Directory::FilenamesType::const_iterator file = Filenames.begin();
278   for(; file != Filenames.end(); ++file)
279     {
280     const char *filename = file->c_str();
281     if( IsKey( filename ) )
282       {
283       keys.push_back( filename );
284       }
285     }
286   assert( keys.size() <= Filenames.size() );
287   return keys;
288 }
289 
290 
GetValue(const char * filename,Tag const & t) const291 const char* Scanner::GetValue(const char *filename, Tag const &t) const
292 {
293   // \precondition
294   assert( Tags.find( t ) != Tags.end() );
295   TagToValue const &ftv = GetMapping(filename);
296   if( ftv.find(t) != ftv.end() )
297     {
298     return ftv.find(t)->second;
299     }
300   return nullptr;
301 }
302 
GetFilenameFromTagToValue(Tag const & t,const char * valueref) const303 const char *Scanner::GetFilenameFromTagToValue(Tag const &t, const char *valueref) const
304 {
305   const char *filenameref = nullptr;
306   if( valueref )
307     {
308     Directory::FilenamesType::const_iterator file = Filenames.begin();
309     size_t len = strlen( valueref );
310     if( len && valueref[ len - 1 ] == ' ' )
311       {
312       --len;
313       }
314     for(; file != Filenames.end() && !filenameref; ++file)
315       {
316       const char *filename = file->c_str();
317       const char * value = GetValue(filename, t);
318       if( value && strncmp(value, valueref, len ) == 0 )
319         {
320         filenameref = filename;
321         }
322       }
323     }
324   return filenameref;
325 }
326 
327 
328 /// Will loop over all files and return a vector of std::strings of filenames
329 /// where value match the reference value 'valueref'
330 Directory::FilenamesType
GetAllFilenamesFromTagToValue(Tag const & t,const char * valueref) const331 Scanner::GetAllFilenamesFromTagToValue(Tag const &t, const char *valueref) const
332 {
333   Directory::FilenamesType theReturn;
334   if( valueref )
335     {
336     const std::string valueref_str = String<>::Trim( valueref );
337     Directory::FilenamesType::const_iterator file = Filenames.begin();
338     for(; file != Filenames.end(); ++file)
339       {
340       const char *filename = file->c_str();
341       const char * value = GetValue(filename, t);
342       const std::string value_str = String<>::Trim( value );
343       if( value_str == valueref_str )
344         {
345         theReturn.push_back( filename );
346         }
347       }
348     }
349   return theReturn;
350 
351 }
352 
GetMappingFromTagToValue(Tag const & t,const char * valueref) const353 Scanner::TagToValue const & Scanner::GetMappingFromTagToValue(Tag const &t, const char *valueref) const
354 {
355   return GetMapping( GetFilenameFromTagToValue(t, valueref) );
356 }
357 
GetValues(Tag const & t) const358 Scanner::ValuesType Scanner::GetValues(Tag const &t) const
359 {
360   ValuesType vt;
361   Directory::FilenamesType::const_iterator file = Filenames.begin();
362   for(; file != Filenames.end(); ++file)
363     {
364     const char *filename = file->c_str();
365     TagToValue const &ttv = GetMapping(filename);
366     if( ttv.find(t) != ttv.end() )
367       {
368       vt.insert( ttv.find(t)->second );
369       }
370     }
371   return vt;
372 }
373 
374 
GetOrderedValues(Tag const & t) const375 Directory::FilenamesType Scanner::GetOrderedValues(Tag const &t) const
376 {
377   Directory::FilenamesType theReturn;
378   Directory::FilenamesType::const_iterator file = Filenames.begin();
379   for(; file != Filenames.end(); ++file)
380     {
381     const char *filename = file->c_str();
382     TagToValue const &ttv = GetMapping(filename);
383     if( ttv.find(t) != ttv.end() )
384       {
385         std::string theVal = std::string(ttv.find(t)->second);
386         if (std::find(theReturn.begin(), theReturn.end(), theVal) == theReturn.end()){
387           theReturn.push_back( theVal );//only add new tags to the list
388         }
389       }
390     }
391   return theReturn;
392 }
393 
ProcessPublicTag(StringFilter & sf,const char * filename)394 void Scanner::ProcessPublicTag(StringFilter &sf, const char *filename)
395 {
396   assert( filename );
397   TagToValue &mapping = Mappings[filename];
398   const File& file = sf.GetFile();
399 
400   const FileMetaInformation & header = file.GetHeader();
401   const DataSet & ds = file.GetDataSet();
402   TagsType::const_iterator tag = Tags.begin();
403   for( ; tag != Tags.end(); ++tag )
404     {
405     if( tag->GetGroup() == 0x2 )
406       {
407       if( header.FindDataElement( *tag ) )
408         {
409         //std::string s;
410         DataElement const & de = header.GetDataElement( *tag );
411         //const ByteValue *bv = de.GetByteValue();
412         ////assert( VR::IsASCII( vr ) );
413         //if( bv ) // Hum, should I store an empty string or what ?
414         //  {
415         //  s = std::string( bv->GetPointer(), bv->GetLength() );
416         //  s.resize( std::min( s.size(), strlen( s.c_str() ) ) );
417         //  }
418         std::string s = sf.ToString(de.GetTag());
419 
420         // Store the potentially new value:
421         Values.insert( s );
422         assert( Values.find( s ) != Values.end() );
423         const char *value = Values.find( s )->c_str();
424         assert( value );
425         mapping.insert(
426           TagToValue::value_type(*tag, value));
427         }
428       }
429     else
430       {
431       if( ds.FindDataElement( *tag ) )
432         {
433         //std::string s;
434         DataElement const & de = ds.GetDataElement( *tag );
435         //const ByteValue *bv = de.GetByteValue();
436         ////assert( VR::IsASCII( vr ) );
437         //if( bv ) // Hum, should I store an empty string or what ?
438         //  {
439         //  s = std::string( bv->GetPointer(), bv->GetLength() );
440         //  s.resize( std::min( s.size(), strlen( s.c_str() ) ) );
441         //  }
442         std::string s = sf.ToString(de.GetTag());
443 
444         // Store the potentially new value:
445         Values.insert( s );
446         assert( Values.find( s ) != Values.end() );
447         const char *value = Values.find( s )->c_str();
448         assert( value );
449         mapping.insert(
450           TagToValue::value_type(*tag, value));
451         }
452       }
453     } // end for
454 }
455 
456 } // end namespace gdcm
457