Program: gdcm
Module: $RCSfile: gdcmDocument.cxx,v $
Language: C++
- Date: $Date: 2004/09/15 03:50:48 $
- Version: $Revision: 1.78 $
+ Date: $Date: 2004/09/22 21:39:42 $
+ Version: $Revision: 1.88 $
Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
l'Image). All rights reserved. See Doc/License.txt or
long beg = ftell(Fp);
lgt -= beg;
- SQDepthLevel = 0;
-
(void)ParseDES( this, beg, lgt, false); // le Load sera fait a la volee
rewind(Fp);
void gdcmDocument::Write(FILE* fp,FileType filetype)
{
/// \todo move the following lines (and a lot of others, to be written)
- /// to a future function CheckAndCorrectHeader
-
- /// WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
- /// no way (check : FileType est un champ de gdcmDocument ...)
- /// a moins de se livrer a un tres complique ajout des champs manquants.
- /// faire un CheckAndCorrectHeader (?)
-
+ /// to a future function CheckAndCorrectHeader
+ /// (necessary if user wants to write a DICOM V3 file
+ /// starting from an ACR-NEMA (V2) gdcmHeader
+
if (filetype == gdcmImplicitVR)
{
std::string implicitVRTransfertSyntax = UI1_2_840_10008_1_2;
* @param value (string) Value to be set
* @param group Group number of the Entry
* @param elem Element number of the Entry
+ * @param VR V(alue) R(epresentation) of the Entry -if private Entry-
* \return pointer to the modified/created Header Entry (NULL when creation
* failed).
- */
-
+ */
gdcmValEntry * gdcmDocument::ReplaceOrCreateByNumber(
std::string const & value,
uint16_t group,
- uint16_t elem )
+ uint16_t elem,
+ std::string const & VR )
{
gdcmValEntry* valEntry = 0;
gdcmDocEntry* currentEntry = GetDocEntryByNumber( group, elem);
if (!currentEntry)
{
- // The entry wasn't present and we simply create the required ValEntry:
- currentEntry = NewDocEntryByNumber(group, elem);
+ // check if (group,element) DictEntry exists
+ // if it doesn't, create an entry in gdcmDictSet::VirtualEntry
+ // and use it
+
+ // Find out if the tag we received is in the dictionaries:
+ gdcmDict *pubDict = gdcmGlobal::GetDicts()->GetDefaultPubDict();
+ gdcmDictEntry *dictEntry = pubDict->GetDictEntryByNumber(group, elem);
+ if (!dictEntry)
+ {
+ currentEntry = NewDocEntryByNumber(group, elem,VR);
+ }
+ else
+ {
+ currentEntry = NewDocEntryByNumber(group, elem);
+ }
+
if (!currentEntry)
{
dbg.Verbose(0, "gdcmDocument::ReplaceOrCreateByNumber: call to"
return valEntry;
}
-/**
+/*
* \brief Modifies the value of a given Header Entry (Dicom Element)
* when it exists. Create it with the given value when unexistant.
- * @param value (string) Value to be set
- * @param group Group number of the Entry
- * @param elem Element number of the Entry
- * @param VR V(alue) R(epresentation) of the Entry -if private Entry-
+ * @param voidArea (binary) value to be set
+ * @param Group Group number of the Entry
+ * @param Elem Element number of the Entry
* \return pointer to the modified/created Header Entry (NULL when creation
* failed).
*/
-
- // TODO : write something clever, using default value for VR
- // to avoid code duplication
- // (I don't know how to tell NewDocEntryByNumber
- // that ReplaceOrCreateByNumber was called with a default value)
-
-gdcmValEntry * gdcmDocument::ReplaceOrCreateByNumber(
- std::string const & value,
+gdcmBinEntry * gdcmDocument::ReplaceOrCreateByNumber(
+ void *voidArea,
+ int lgth,
uint16_t group,
uint16_t elem,
std::string const & VR )
{
- gdcmValEntry* valEntry = 0;
+ gdcmBinEntry* binEntry = 0;
gdcmDocEntry* currentEntry = GetDocEntryByNumber( group, elem);
-
if (!currentEntry)
{
+
// check if (group,element) DictEntry exists
// if it doesn't, create an entry in gdcmDictSet::VirtualEntry
// and use it
// Find out if the tag we received is in the dictionaries:
gdcmDict *pubDict = gdcmGlobal::GetDicts()->GetDefaultPubDict();
gdcmDictEntry *dictEntry = pubDict->GetDictEntryByNumber(group, elem);
+
if (!dictEntry)
{
- currentEntry = NewDocEntryByNumber(group, elem,VR);
+ currentEntry = NewDocEntryByNumber(group, elem, VR);
}
else
{
currentEntry = NewDocEntryByNumber(group, elem);
}
-
if (!currentEntry)
{
dbg.Verbose(0, "gdcmDocument::ReplaceOrCreateByNumber: call to"
" NewDocEntryByNumber failed.");
return NULL;
}
- valEntry = new gdcmValEntry(currentEntry);
- if ( !AddEntry(valEntry))
+ binEntry = new gdcmBinEntry(currentEntry);
+ if ( !AddEntry(binEntry))
{
dbg.Verbose(0, "gdcmDocument::ReplaceOrCreateByNumber: AddEntry"
" failed allthough this is a creation.");
}
else
{
- valEntry = dynamic_cast< gdcmValEntry* >(currentEntry);
- if ( !valEntry ) // Euuuuh? It wasn't a ValEntry
- // then we change it to a ValEntry ?
+ binEntry = dynamic_cast< gdcmBinEntry* >(currentEntry);
+ if ( !binEntry ) // Euuuuh? It wasn't a BinEntry
+ // then we change it to a BinEntry ?
// Shouldn't it be considered as an error ?
{
- // We need to promote the gdcmDocEntry to a gdcmValEntry:
- valEntry = new gdcmValEntry(currentEntry);
+ // We need to promote the gdcmDocEntry to a gdcmBinEntry:
+ binEntry = new gdcmBinEntry(currentEntry);
if (!RemoveEntry(currentEntry))
{
dbg.Verbose(0, "gdcmDocument::ReplaceOrCreateByNumber: removal"
" of previous DocEntry failed.");
return NULL;
}
- if ( !AddEntry(valEntry))
+ if ( !AddEntry(binEntry))
{
dbg.Verbose(0, "gdcmDocument::ReplaceOrCreateByNumber: adding"
- " promoted ValEntry failed.");
+ " promoted BinEntry failed.");
return NULL;
}
}
}
- SetEntryByNumber(value, group, elem);
-
- return valEntry;
-}
-
-/*
- * \brief Modifies the value of a given Header Entry (Dicom Element)
- * when it exists. Create it with the given value when unexistant.
- * @param voidArea (binary) value to be set
- * @param Group Group number of the Entry
- * @param Elem Element number of the Entry
- * \return pointer to the modified/created Header Entry (NULL when creation
- * failed).
- */
-gdcmBinEntry * gdcmDocument::ReplaceOrCreateByNumber(
- void *voidArea,
- int lgth,
- uint16_t group,
- uint16_t elem)
-{
- gdcmBinEntry* b = 0;
- gdcmDocEntry* a = GetDocEntryByNumber( group, elem);
- if (!a)
- {
- a = NewBinEntryByNumber(group, elem);
- if (!a)
- {
- return 0;
- }
-
- b = new gdcmBinEntry(a);
- AddEntry(b);
- b->SetVoidArea(voidArea);
- }
-
SetEntryByNumber(voidArea, lgth, group, elem);
- return b;
+ return binEntry;
}
return elem->GetVR();
}
-
/**
* \brief Searches within Header Entries (Dicom Elements) parsed with
* the public and private dictionaries
gdcmBinEntry* a = (gdcmBinEntry *)TagHT[key];
a->SetVoidArea(content);
a->SetLength(lgth);
+ a->SetValue(GDCM_BINLOADED);
return true;
}
delete[] a;
return NULL;
}
- /// \TODO Drop any already existing void area! JPR
+ /// \todo Drop any already existing void area! JPR
if( !SetEntryVoidAreaByNumber( a, group, elem ) );
{
dbg.Verbose(0, "gdcmDocument::LoadEntryVoidArea setting failed.");
}
-
return a;
}
/**
gdcmDocEntry *newDocEntry = 0;
unsigned long l = 0;
- int depth = set->GetDepthLevel();
while (true)
{
if ( !delim_mode && (ftell(Fp)-offset) >= l_max)
if ( gdcmGlobal::GetVR()->IsVROfGdcmStringRepresentable(vr) )
{
- /////// ValEntry
+ /////////////////////// ValEntry
gdcmValEntry* newValEntry =
new gdcmValEntry( newDocEntry->GetDictEntry() );
newValEntry->Copy( newDocEntry );
- newValEntry->SetKey( set->GetBaseTagKey() + newValEntry->GetKey() );
+
+ // When "set" is a gdcmDocument, then we are at the top of the
+ // hierarchy and the Key is simply of the form ( group, elem )...
+ if (gdcmDocument* dummy = dynamic_cast< gdcmDocument* > ( set ) )
+ {
+ (void)dummy;
+ newValEntry->SetKey( newValEntry->GetKey() );
+ }
+ // ...but when "set" is a gdcmSQItem, we are inserting this new
+ // valEntry in a sequence item. Hence the key has the
+ // generalized form (refer to \ref gdcmBaseTagKey):
+ if (gdcmSQItem* parentSQItem = dynamic_cast< gdcmSQItem* > ( set ) )
+ {
+ newValEntry->SetKey( parentSQItem->GetBaseTagKey()
+ + newValEntry->GetKey() );
+ }
+
set->AddEntry( newValEntry );
LoadDocEntry( newValEntry );
if (newValEntry->IsItemDelimitor())
"nor BinEntry. Probably unknown VR.");
}
- ////// BinEntry or UNKOWN VR:
+ //////////////////// BinEntry or UNKOWN VR:
gdcmBinEntry* newBinEntry =
new gdcmBinEntry( newDocEntry->GetDictEntry() );
newBinEntry->Copy( newDocEntry );
- newBinEntry->SetKey( set->GetBaseTagKey() + newBinEntry->GetKey() );
+
+ // When "this" is a gdcmDocument the Key is simply of the
+ // form ( group, elem )...
+ if (gdcmDocument* dummy = dynamic_cast< gdcmDocument* > ( set ) )
+ {
+ (void)dummy;
+ newBinEntry->SetKey( newBinEntry->GetKey() );
+ }
+ // but when "this" is a SQItem, we are inserting this new
+ // valEntry in a sequence item, and the kay has the
+ // generalized form (refer to \ref gdcmBaseTagKey):
+ if (gdcmSQItem* parentSQItem = dynamic_cast< gdcmSQItem* > ( set ) )
+ {
+ newBinEntry->SetKey( parentSQItem->GetBaseTagKey()
+ + newBinEntry->GetKey() );
+ }
+
set->AddEntry( newBinEntry );
LoadDocEntry( newBinEntry );
}
}
// no other way to create it ...
gdcmSeqEntry* newSeqEntry =
- new gdcmSeqEntry( newDocEntry->GetDictEntry(),
- set->GetDepthLevel() );
+ new gdcmSeqEntry( newDocEntry->GetDictEntry() );
newSeqEntry->Copy( newDocEntry );
newSeqEntry->SetDelimitorMode( delim_mode );
- newSeqEntry->SetDepthLevel( depth );
- newSeqEntry->SetKey( set->GetBaseTagKey() + newSeqEntry->GetKey() );
+
+ // At the top of the hierarchy, stands a gdcmDocument. When "set"
+ // is a gdcmDocument, then we are building the first depth level.
+ // Hence the gdcmSeqEntry we are building simply has a depth
+ // level of one:
+ if (gdcmDocument* dummy = dynamic_cast< gdcmDocument* > ( set ) )
+ {
+ (void)dummy;
+ newSeqEntry->SetDepthLevel( 1 );
+ newSeqEntry->SetKey( newSeqEntry->GetKey() );
+ }
+ // But when "set" is allready a SQItem, we are building a nested
+ // sequence, and hence the depth level of the new gdcmSeqEntry
+ // we are building, is one level deeper:
+ if (gdcmSQItem* parentSQItem = dynamic_cast< gdcmSQItem* > ( set ) )
+ {
+ newSeqEntry->SetDepthLevel( parentSQItem->GetDepthLevel() + 1 );
+ newSeqEntry->SetKey( parentSQItem->GetBaseTagKey()
+ + newSeqEntry->GetKey() );
+ }
if ( l != 0 )
{ // Don't try to parse zero-length sequences
// When we find a BinEntry not very much can be done :
if (gdcmBinEntry* binEntryPtr = dynamic_cast< gdcmBinEntry* >(entry) )
{
- s << "gdcm::Loaded (BinEntry)";
+ s << GDCM_BINLOADED;
binEntryPtr->SetValue(s.str());
LoadEntryVoidArea(binEntryPtr); // last one, not to erase length !
return;
/**
* \brief Parse pixel data from disk for multi-fragment Jpeg/Rle files
* No other way so 'skip' the Data
- *
*/
void gdcmDocument::Parse7FE0 ()
{
}
}
+/**
+ * \brief Walk recursively the given \ref gdcmDocEntrySet, and feed
+ * the given hash table (\ref TagDocEntryHT) with all the
+ * \ref gdcmDocEntry (Dicom entries) encountered.
+ * This method does the job for \ref BuildFlatHashTable.
+ * @param builtHT Where to collect all the \ref gdcmDocEntry encountered
+ * when recursively walking the given set.
+ * @param set The structure to be traversed (recursively).
+ */
+void gdcmDocument::BuildFlatHashTableRecurse( TagDocEntryHT& builtHT,
+ gdcmDocEntrySet* set )
+{
+ if (gdcmElementSet* elementSet = dynamic_cast< gdcmElementSet* > ( set ) )
+ {
+ TagDocEntryHT* currentHT = elementSet->GetTagHT();
+ for( TagDocEntryHT::const_iterator i = currentHT->begin();
+ i != currentHT->end();
+ ++i)
+ {
+ gdcmDocEntry* entry = i->second;
+ if ( gdcmSeqEntry* seqEntry = dynamic_cast<gdcmSeqEntry*>(entry) )
+ {
+ ListSQItem& items = seqEntry->GetSQItems();
+ for( ListSQItem::const_iterator item = items.begin();
+ item != items.end();
+ ++item)
+ {
+ BuildFlatHashTableRecurse( builtHT, *item );
+ }
+ continue;
+ }
+ builtHT[entry->GetKey()] = entry;
+ }
+ return;
+ }
+
+ if (gdcmSQItem* SQItemSet = dynamic_cast< gdcmSQItem* > ( set ) )
+ {
+ ListDocEntry& currentList = SQItemSet->GetDocEntries();
+ for (ListDocEntry::iterator i = currentList.begin();
+ i != currentList.end();
+ ++i)
+ {
+ gdcmDocEntry* entry = *i;
+ if ( gdcmSeqEntry* seqEntry = dynamic_cast<gdcmSeqEntry*>(entry) )
+ {
+ ListSQItem& items = seqEntry->GetSQItems();
+ for( ListSQItem::const_iterator item = items.begin();
+ item != items.end();
+ ++item)
+ {
+ BuildFlatHashTableRecurse( builtHT, *item );
+ }
+ continue;
+ }
+ builtHT[entry->GetKey()] = entry;
+ }
+
+ }
+}
+
+/**
+ * \brief Build a \ref TagDocEntryHT (i.e. a std::map<>) from the current
+ * gdcmDocument.
+ *
+ * The structure used by a gdcmDocument (through \ref gdcmElementSet),
+ * in order to old the parsed entries of a Dicom header, is a recursive
+ * one. This is due to the fact that the sequences (when present)
+ * can be nested. Additionaly, the sequence items (represented in
+ * gdcm as \ref gdcmSQItem) add an extra complexity to the data
+ * structure. Hence, a gdcm user whishing to visit all the entries of
+ * a Dicom header will need to dig in the gdcm internals (which
+ * implies exposing all the internal data structures to the API).
+ * In order to avoid this burden to the user, \ref BuildFlatHashTable
+ * recursively builds a temporary hash table, which holds all the
+ * Dicom entries in a flat structure (a \ref TagDocEntryHT i.e. a
+ * std::map<>).
+ * \warning Of course there is NO integrity constrain between the
+ * returned \ref TagDocEntryHT and the \ref gdcmElementSet used
+ * to build it. Hence if the underlying \ref gdcmElementSet is
+ * altered, then it is the caller responsability to invoke
+ * \ref BuildFlatHashTable again...
+ * @return The flat std::map<> we juste build.
+ */
+TagDocEntryHT* gdcmDocument::BuildFlatHashTable()
+{
+ TagDocEntryHT* FlatHT = new TagDocEntryHT;
+ BuildFlatHashTableRecurse( *FlatHT, this );
+ return FlatHT;
+}
+
/**