/*========================================================================= Program: GDCM (Grassroots DICOM). A DICOM library Copyright (c) 2006-2011 Mathieu Malaterre All rights reserved. See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #ifndef GDCMDATASET_H #define GDCMDATASET_H #include "gdcmDataElement.h" #include "gdcmTag.h" #include "gdcmVR.h" #include "gdcmElement.h" #include "gdcmMediaStorage.h" #include #include namespace gdcm_ns { class GDCM_EXPORT DataElementException : public std::exception {}; class PrivateTag; /** * \brief Class to represent a Data Set (which contains Data Elements) * \details A Data Set represents an instance of a real world Information Object * \note * DATA SET: * Exchanged information consisting of a structured set of Attribute values * directly or indirectly related to Information Objects. The value of each * Attribute in a Data Set is expressed as a Data Element. * A collection of Data Elements ordered by increasing Data Element Tag * number that is an encoding of the values of Attributes of a real world * object. * \note * Implementation note. If one do: * DataSet ds; * ds.SetLength(0); * ds.Read(is); * setting length to 0 actually means try to read is as if it was a root * DataSet. Other value are undefined (nested dataset with undefined length) * or defined length (different from 0) means nested dataset with defined * length. * * \warning * a DataSet does not have a Transfer Syntax type, only a File does. */ class GDCM_EXPORT DataSet { friend class CSAHeader; public: typedef std::set DataElementSet; typedef DataElementSet::const_iterator ConstIterator; typedef DataElementSet::iterator Iterator; typedef DataElementSet::size_type SizeType; //typedef typename DataElementSet::iterator iterator; ConstIterator Begin() const { return DES.begin(); } Iterator Begin() { return DES.begin(); } ConstIterator End() const { return DES.end(); } Iterator End() { return DES.end(); } const DataElementSet &GetDES() const { return DES; } DataElementSet &GetDES() { return DES; } void Clear() { DES.clear(); assert( DES.empty() ); } SizeType Size() const { return DES.size(); } void Print(std::ostream &os, std::string const &indent = "") const { // CT_Phillips_JPEG2K_Decompr_Problem.dcm has a SQ of length == 0 //int s = DES.size(); //assert( s ); //std::copy(DES.begin(), DES.end(), // std::ostream_iterator(os, "\n")); ConstIterator it = DES.begin(); for( ; it != DES.end(); ++it) { os << indent << *it << "\n"; } } template unsigned int ComputeGroupLength(Tag const &tag) const { assert( tag.GetElement() == 0x0 ); const DataElement r(tag); ConstIterator it = DES.find(r); unsigned int res = 0; for( ++it; it != DES.end() && it->GetTag().GetGroup() == tag.GetGroup(); ++it) { assert( it->GetTag().GetElement() != 0x0 ); assert( it->GetTag().GetGroup() == tag.GetGroup() ); res += it->GetLength(); } return res; } template VL GetLength() const { if( DES.empty() ) return 0; assert( !DES.empty() ); VL ll = 0; assert( ll == 0 ); ConstIterator it = DES.begin(); for( ; it != DES.end(); ++it) { assert( !(it->GetLength().IsUndefined()) ); if ( it->GetTag() != Tag(0xfffe,0xe00d) ) { ll += it->GetLength(); } } return ll; } /// Insert a DataElement in the DataSet. /// \warning: Tag need to be >= 0x8 to be considered valid data element void Insert(const DataElement& de) { // FIXME: there is a special case where a dataset can have value < 0x8, see: // $ gdcmdump --csa gdcmData/SIEMENS-JPEG-CorruptFrag.dcm if( de.GetTag().GetGroup() >= 0x0008 || de.GetTag().GetGroup() == 0x4 ) { // prevent user error: if( de.GetTag() == Tag(0xfffe,0xe00d) || de.GetTag() == Tag(0xfffe,0xe0dd) || de.GetTag() == Tag(0xfffe,0xe000) ) { } else { InsertDataElement( de ); } } else { gdcmErrorMacro( "Cannot add element with group < 0x0008 and != 0x4 in the dataset: " << de.GetTag() ); } } /// Replace a dataelement with another one void Replace(const DataElement& de) { ConstIterator it = DES.find(de); if( it != DES.end() ) { // detect loop: gdcmAssertAlwaysMacro( &*it != &de ); DES.erase(it); } DES.insert(de); } /// Only replace a DICOM attribute when it is missing or empty void ReplaceEmpty(const DataElement& de) { ConstIterator it = DES.find(de); if( it != DES.end() && it->IsEmpty() ) { // detect loop: gdcmAssertAlwaysMacro( &*it != &de ); DES.erase(it); } DES.insert(de); } /// Completely remove a dataelement from the dataset SizeType Remove(const Tag& tag) { DataElementSet::size_type count = DES.erase(tag); assert( count == 0 || count == 1 ); return count; } /// Return the DataElement with Tag 't' /// \warning: /// This only search at the 'root level' of the DataSet //DataElement& GetDataElement(const Tag &t) { // DataElement r(t); // Iterator it = DES.find(r); // if( it != DES.end() ) // return *it; // return GetDEEnd(); // } const DataElement& GetDataElement(const Tag &t) const { const DataElement r(t); ConstIterator it = DES.find(r); if( it != DES.end() ) return *it; return GetDEEnd(); } const DataElement& operator[] (const Tag &t) const { return GetDataElement(t); } const DataElement& operator() (uint16_t group, uint16_t element) const { return GetDataElement( Tag(group,element) ); } /// Return the private creator of the private tag 't': std::string GetPrivateCreator(const Tag &t) const; /// Look up if private tag 't' is present in the dataset: bool FindDataElement(const PrivateTag &t) const; /// Return the dataelement const DataElement& GetDataElement(const PrivateTag &t) const; // DUMB: this only search within the level of the current DataSet bool FindDataElement(const Tag &t) const { const DataElement r(t); //ConstIterator it = DES.find(r); if( DES.find(r) != DES.end() ) { return true; } return false; } // WARNING: // This only search at the same level as the DataSet is ! const DataElement& FindNextDataElement(const Tag &t) const { const DataElement r(t); ConstIterator it = DES.lower_bound(r); if( it != DES.end() ) return *it; return GetDEEnd(); } /// Returns if the dataset is empty bool IsEmpty() const { return DES.empty(); }; DataSet& operator=(DataSet const &) = default; /* template void ExecuteOperation(TOperation & operation) { assert( !DES.empty() ); DataElementSet::iterator it = Begin(); for( ; it != End(); ++it) { DataElement &de = (DataElement&)*it; operation( de ); } } */ template std::istream &ReadNested(std::istream &is); template std::istream &Read(std::istream &is); template std::istream &ReadUpToTag(std::istream &is, const Tag &t, std::set const & skiptags); template std::istream &ReadUpToTagWithLength(std::istream &is, const Tag &t, std::set const & skiptags, VL & length); template std::istream &ReadSelectedTags(std::istream &is, const std::set & tags, bool readvalues = true); template std::istream &ReadSelectedTagsWithLength(std::istream &is, const std::set & tags, VL & length, bool readvalues = true); template std::istream &ReadSelectedPrivateTags(std::istream &is, const std::set & tags, bool readvalues = true); template std::istream &ReadSelectedPrivateTagsWithLength(std::istream &is, const std::set & tags, VL & length, bool readvalues = true); template std::ostream const &Write(std::ostream &os) const; template std::istream &ReadWithLength(std::istream &is, VL &length); MediaStorage GetMediaStorage() const; protected: /* GetDEEnd is a Win32 only issue, one cannot use a dllexported * static member data in an inline function, otherwise symbol * will get reported as missing in any dll using the inlined function */ const DataElement& GetDEEnd() const; // This function is not safe, it does not check for the value of the tag // so depending whether we are getting called from a dataset or file meta header // the condition is different void InsertDataElement(const DataElement& de) { //if( de.GetTag() == Tag(0xfffe,0xe00d) ) return; //if( de.GetTag() == Tag(0xfffe,0xe0dd) ) return; #ifndef NDEBUG std::pair pr = DES.insert(de); if( pr.second == false ) { gdcmWarningMacro( "DataElement: " << de << " was already found, skipping duplicate entry.\n" "Original entry kept is: " << *pr.first ); } #else DES.insert(de); #endif assert( de.IsEmpty() || de.GetVL() == de.GetValue().GetLength() ); } protected: // Internal function, that will compute the actual Tag (if found) of // a requested Private Tag (XXXX,YY,"PRIVATE") Tag ComputeDataElement(const PrivateTag & t) const; private: DataElementSet DES; static DataElement DEEnd; friend std::ostream& operator<<(std::ostream &_os, const DataSet &); }; //----------------------------------------------------------------------------- inline std::ostream& operator<<(std::ostream &os, const DataSet &val) { val.Print(os); return os; } #if defined(SWIGPYTHON) || defined(SWIGCSHARP) || defined(SWIGJAVA) || defined(SWIGPHP) /* * HACK: I need this temp class to be able to manipulate a std::set from python, * swig does not support wrapping of simple class like std::set... */ class SWIGDataSet { public: SWIGDataSet(DataSet &des):Internal(des),it(des.Begin()) {} const DataElement& GetCurrent() const { return *it; } void Start() { it = Internal.Begin(); } bool IsAtEnd() const { return it == Internal.End(); } void Next() { ++it; } private: DataSet & Internal; DataSet::ConstIterator it; }; #endif /* SWIG */ /** * \example SimplePrint.cs * This is a C# example on how to use gdcm::SWIGDataSet */ } // end namespace gdcm_ns #include "gdcmDataSet.txx" #endif //GDCMDATASET_H