/*========================================================================= 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 GDCMDATAELEMENT_H #define GDCMDATAELEMENT_H #include "gdcmTag.h" #include "gdcmVL.h" #include "gdcmVR.h" #include "gdcmByteValue.h" #include "gdcmSmartPointer.h" #include namespace gdcm_ns { // Data Element // Contains multiple fields: // -> Tag // -> Optional VR (Explicit Transfer Syntax) // -> ValueLength // -> Value // TODO: This class SHOULD be pure virtual. I don't want a user // to shoot himself in the foot. class SequenceOfItems; class SequenceOfFragments; /** * \brief Class to represent a Data Element * either Implicit or Explicit * * \details * DATA ELEMENT: * A unit of information as defined by a single entry in the data dictionary. * An encoded Information Object Definition (IOD) Attribute that is composed * of, at a minimum, three fields: a Data Element Tag, a Value Length, and a * Value Field. For some specific Transfer Syntaxes, a Data Element also * contains a VR Field where the Value Representation of that Data Element is * specified explicitly. * * Design: * \li A DataElement in GDCM always store VL (Value Length) on a 32 bits integer even when VL is 16 bits * \li A DataElement always store the VR even for Implicit TS, in which case VR is defaulted to VR::INVALID * \li For Item start/end (See 0xfffe tags), Value is NULL * * \see ExplicitDataElement ImplicitDataElement */ class GDCM_EXPORT DataElement { public: DataElement(const Tag& t = Tag(0), const VL& vl = 0, const VR &vr = VR::INVALID):TagField(t),ValueLengthField(vl),VRField(vr),ValueField(nullptr) {} //DataElement( Attribute const &att ); friend std::ostream& operator<<(std::ostream &_os, const DataElement &_val); /// Get Tag const Tag& GetTag() const { return TagField; } Tag& GetTag() { return TagField; } /// Set Tag /// Use with cautious (need to match Part 6) void SetTag(const Tag &t) { TagField = t; } /// Get VL const VL& GetVL() const { return ValueLengthField; } VL& GetVL() { return ValueLengthField; } /// Set VL /// Use with cautious (need to match Part 6), advanced user only /// \see SetByteValue void SetVL(const VL &vl) { ValueLengthField = vl; } void SetVLToUndefined(); /// Get VR /// do not set VR::SQ on bytevalue data element VR const &GetVR() const { return VRField; } /// Set VR /// Use with cautious (need to match Part 6), advanced user only /// \pre vr is a VR::VRALL (not a dual one such as OB_OW) void SetVR(VR const &vr) { if( vr.IsVRFile() ) VRField = vr; } /// Set/Get Value (bytes array, SQ of items, SQ of fragments): Value const &GetValue() const { gdcmAssertAlwaysMacro(ValueField); return *ValueField; } Value &GetValue() { gdcmAssertAlwaysMacro(ValueField); return *ValueField; } /// \warning you need to set the ValueLengthField explicitly void SetValue(Value const & vl) { //assert( ValueField == 0 ); ValueField = vl; ValueLengthField = vl.GetLength(); } /// Check if Data Element is empty bool IsEmpty() const { return ValueField == nullptr || (GetByteValue() && GetByteValue()->IsEmpty()); } /// Make Data Element empty (no Value) void Empty() { ValueField = nullptr; ValueLengthField = 0; } /// Clear Data Element (make Value empty and invalidate Tag & VR) void Clear() { TagField = 0; VRField = VR::INVALID; ValueField = nullptr; ValueLengthField = 0; } // Helper: /// Set the byte value /// \warning user need to read DICOM standard for an understanding of: /// * even padding /// * \0 vs space padding /// By default even padding is achieved using \0 regardless of the of VR void SetByteValue(const char *array, VL length) { ByteValue *bv = new ByteValue(array,length); SetValue( *bv ); } /// Return the Value of DataElement as a ByteValue (if possible) /// \warning: You need to check for NULL return value const ByteValue* GetByteValue() const { // Get the raw pointer from the gdcm::SmartPointer const ByteValue *bv = dynamic_cast(ValueField.GetPointer()); return bv; // Will return NULL if not ByteValue } /// Interpret the Value stored in the DataElement. This is more robust (but also more /// expensive) to call this function rather than the simplest form: GetSequenceOfItems() /// It also return NULL when the Value is NOT of type SequenceOfItems /// \warning in case GetSequenceOfItems() succeed the function return this value, otherwise /// it creates a new SequenceOfItems, you should handle that in your case, for instance: /// SmartPointer sqi = de.GetValueAsSQ(); SmartPointer GetValueAsSQ() const; /// Return the Value of DataElement as a Sequence Of Fragments (if possible) /// \warning: You need to check for NULL return value const SequenceOfFragments* GetSequenceOfFragments() const; SequenceOfFragments* GetSequenceOfFragments(); /// return if Value Length if of undefined length bool IsUndefinedLength() const { return ValueLengthField.IsUndefined(); } DataElement(const DataElement &_val) { if( this != &_val) { *this = _val; } } bool operator<(const DataElement &de) const { return GetTag() < de.GetTag(); } DataElement &operator=(const DataElement &) = default; bool operator==(const DataElement &de) const { bool b = TagField == de.TagField && ValueLengthField == de.ValueLengthField && VRField == de.VRField; if( !ValueField && !de.ValueField ) { return b; } if( ValueField && de.ValueField ) { return b && (*ValueField == *de.ValueField); } // ValueField != de.ValueField return false; } // The following functionalities are dependent on: // # The Transfer Syntax: Explicit or Implicit // # The Byte encoding: Little Endian / Big Endian /* * The following was inspired by a C++ idiom: Curiously Recurring Template Pattern * Ref: http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern * The typename TDE is typically a derived class *without* any data * while TSwap is a simple template parameter to achieve byteswapping (and allow factorization of * highly identical code) */ template VL GetLength() const { return static_cast(this)->GetLength(); } template std::istream &Read(std::istream &is) { return static_cast(this)->template Read(is); } template std::istream &ReadOrSkip(std::istream &is, std::set const &skiptags) { (void)skiptags; return static_cast(this)->template Read(is); } template std::istream &ReadPreValue(std::istream &is, std::set const &skiptags) { (void)skiptags; return static_cast(this)->template ReadPreValue(is); } template std::istream &ReadValue(std::istream &is, std::set const &skiptags) { (void)skiptags; return static_cast(this)->template ReadValue(is); } template std::istream &ReadValueWithLength(std::istream &is, VL & length, std::set const &skiptags) { (void)skiptags; return static_cast(this)->template ReadValueWithLength(is, length); } template std::istream &ReadWithLength(std::istream &is, VL &length) { return static_cast(this)->template ReadWithLength(is,length); } template const std::ostream &Write(std::ostream &os) const { return static_cast(this)->template Write(os); } protected: Tag TagField; // This is the value read from the file, might be different from the length of Value Field VL ValueLengthField; // Can be 0xFFFFFFFF // Value Representation VR VRField; typedef SmartPointer ValuePtr; ValuePtr ValueField; void SetValueFieldLength( VL vl, bool readvalues ); }; //----------------------------------------------------------------------------- inline std::ostream& operator<<(std::ostream &os, const DataElement &val) { os << val.TagField; os << "\t" << val.VRField; os << "\t" << val.ValueLengthField; if( val.ValueField ) { val.ValueField->Print( os << "\t" ); } return os; } inline bool operator!=(const DataElement& lhs, const DataElement& rhs) { return ! ( lhs == rhs ); } } // end namespace gdcm_ns #endif //GDCMDATAELEMENT_H