DYT/Tool/3rdParty_x64/include/dcmtk/dcmiod/iodutil.h
2024-11-22 23:19:31 +08:00

905 lines
40 KiB
C++

/*
*
* Copyright (C) 2015-2016, Open Connections GmbH
* All rights reserved. See COPYRIGHT file for details.
*
* This software and supporting documentation are maintained by
*
* OFFIS e.V.
* R&D Division Health
* Escherweg 2
* D-26121 Oldenburg, Germany
*
*
* Module: dcmiod
*
* Author: Michael Onken
*
* Purpose: Static helper functionality for dcmiod module
*
*/
#ifndef IODUTIL_H
#define IODUTIL_H
#include "dcmtk/config/osconfig.h"
#include "dcmtk/ofstd/oftraits.h" // for OFremove_pointer
#include "dcmtk/ofstd/ofstring.h"
#include "dcmtk/dcmdata/dcelem.h"
#include "dcmtk/dcmdata/dcsequen.h"
#include "dcmtk/dcmdata/dcdatset.h"
#include "dcmtk/dcmdata/dcdatutl.h"
#include "dcmtk/dcmiod/ioddef.h"
#include "dcmtk/dcmiod/iodrules.h"
#include "dcmtk/dcmiod/iodtypes.h"
/** Class with helper functions used within the dcmiod module (and beyond)
*/
class DCMTK_DCMIOD_EXPORT DcmIODUtil
{
public:
/** Get element from dataset and check it for correct value multiplicity
* and type.
* @param dataset DICOM dataset from which the element should be retrieved.
* (Would be 'const' if the methods from 'dcmdata' would also
* (be 'const'.)
* @param delem DICOM element used to store the value (always creates
* a copy of the dataset element's value)
* @param vm Value multiplicity (according to the data dictionary) to be
* checked for. (See DcmElement::checkVM() for a list of valid
* values.) Interpreted as cardinality (number of items) for
* sequence attributes.
* @param type Value type (valid value: "1", "1C", "2", something else
* which is not checked)
* @param moduleName Optional module name to be printed (NULL: no module
* printed at all)
* @return EC_Normal if element could be retrieved and value is correct,
* an error code otherwise
*/
static OFCondition getAndCheckElementFromDataset(DcmItem &dataset,
DcmElement &delem,
const OFString &vm,
const OFString &type,
const char *moduleName = NULL);
/** Get element from dataset and check it for correct value multiplicity and
* type.
* @param dataset DICOM dataset from which the element should be retrieved.
* (Would be 'const' if the methods from 'dcmdata' would also
* be 'const')
* @param tagKey Tag key of the element to get
* @param delem DICOM element that is set to a copy of the dataset's
* orinal element
* @param vm Value multiplicity (according to the data dictionary) to be
* checked for. (See DcmElement::checkVM() for a list of valid
* values.) Interpreted as cardinality (number of items) for
* sequence attributes.
* @param type Value type (valid value: "1", "1C", "2", something else
* which is not checked)
* @param moduleName Optional module name to be printed (NULL: no module
* printed at all)
* @return EC_Normal if element could be retrieved and value is correct, an
* error code otherwise
*/
static OFCondition getAndCheckElementFromDataset(DcmItem &dataset,
const DcmTagKey& tagKey,
DcmElement*& delem,
const OFString &vm,
const OFString &type,
const char *moduleName = NULL);
/** Get element from dataset and check it for correct value multiplicity
* and type.
* @param dataset DICOM dataset from which the element should be retrieved.
* (Would be 'const' if the methods from 'dcmdata' would also
* be 'const'.)
* @param delem DICOM element used to store the value (always creates
* a copy of the value from the original element)
* @param rule Rule describing parameters to be checked on element
* @return EC_Normal if element could be retrieved and value is correct, an
* error code otherwise
*/
static OFCondition getAndCheckElementFromDataset(DcmItem &dataset,
DcmElement &delem,
const IODRule* rule);
/** Get element from dataset and check it for correct value multiplicity
* and type.
* @param dataset DICOM dataset from which the element should be retrieved.
* (Would be 'const' if the methods from 'dcmdata' would also
* be 'const')
* @param delem DICOM element that is set to a copy of the dataset's
* orinal element
* @param rule Rule describing parameters to be checked on element.
* @return EC_Normal if element could be retrieved and value is correct, an
* error code otherwise
*/
static OFCondition getAndCheckElementFromDataset(DcmItem &dataset,
DcmElement*& delem,
const IODRule* rule);
/** Get current date in DICOM 'DA' format (YYYYMMDD)
* @param dateString String used to store the current date.
* (empty string if current date could not be retrieved)
* @return Resulting character string
*/
static const OFString& currentDate(OFString &dateString);
/** Get current time in DICOM 'TM' format (HHMMSS)
* The optional UTC notation (e.g.\ +0100) is currently not supported.
* @param timeString String used to store the current time
* (empty string if current time could not be retrieved)
* @return Resulting character string, empty if time could not be retrieved
*/
static const OFString& currentTime(OFString &timeString);
/** Copy given element to the dataset
* The element is only added if 'result' is EC_Normal. A copy of the given
* element is created, i.e.\ the caller is responsible for deleting the
* original element handed to this function (if desired).
* @param result Reference to status variable (checked before adding and
* updated afterwards!)
* @param dataset Reference to DICOM dataset to which the element should
* be added
* @param delem Reference to DICOM element which should be added; a
* copy is created so caller is responsible for deleting delem (if
* desired).
* @param vm Value multiplicity (according to the data dictionary) to be
* checked for. (See DcmElement::checkVM() for a list of valid
* values.). Interpreted as cardinality (number of items) for
* sequence attributes.
* @param type Value type (valid value: "1", "2" or something else which
* is not checked)
* @param moduleName Optional module name to be printed (NULL: no module
* printed at all)
* @return Current value of 'result', EC_Normal if successful,
* an error code otherwise
*/
static OFCondition copyElementToDataset(OFCondition &result,
DcmItem &dataset,
const DcmElement &delem,
const OFString &vm,
const OFString &type,
const char *moduleName = NULL);
/** Copy given element to the dataset. The element is only added if 'result'
* is EC_Normal.
* @param result reference to status variable (checked before adding and
* updated afterwards!)
* @param dataset reference to DICOM dataset to which the element
* should be added
* @param delem reference to DICOM element which should be added; the value
* is copied from the original dataset's element
* @param rule Rule describing parameters to be checked on element.
* @return Current value of 'result', EC_Normal if successful, an error code
* otherwise
*/
static OFCondition copyElementToDataset(OFCondition &result,
DcmItem &dataset,
const DcmElement &delem,
const IODRule* rule);
/** Add given element to the dataset
* The element is only added if 'result' is EC_Normal and the 'delem'
* pointer is not NULL.
* @param result Reference to status variable (checked before adding and
* updated afterwards!)
* @param dataset Reference to DICOM dataset to which the element should
* be added
* @param delem Pointer to DICOM element which should be added. The element
* is always consumed by this function, i.e.\ insertion was successful
* and therefore ownership is transferred to the dataset, or the
* element is deleted from memory if it could not be inserted.
* @param rule Rule describing parameters to be checked on element.
* @return Current value of 'result', EC_Normal if successful, an error code otherwise
*/
static OFCondition addElementToDataset(OFCondition &result,
DcmItem &dataset,
DcmElement *delem,
const IODRule* rule);
/** Check element value for correct value multiplicity and type.
* @param delem Pointer to DICOM element to be checked (might be NULL)
* @param tagKey DICOM tag of the DICOM element the parameter 'delem' points to
* @param vm Value multiplicity (according to the data dictionary) to be checked for.
* (See DcmElement::checkVM() for a list of valid values.)
* Interpreted as cardinality (number of items) for sequence attributes.
* @param type Value type (valid value: "1", "1C", "2", something else)
* @param searchCond Optional flag indicating the status of a previous 'search' function call
* @param moduleName Optional module name to be printed (default: "IOD" if NULL)
* @return EC_Normal if element value is correct, error otherwise
*/
static OFCondition checkElementValue(const DcmElement *delem,
const DcmTagKey &tagKey,
const OFString &vm,
const OFString &type,
const OFCondition &searchCond = EC_Normal,
const char *moduleName = NULL);
/** Check element value for correct value multiplicity and type.
* @param delem DICOM element to be checked
* @param vm Value multiplicity (according to the data dictionary) to be checked for.
* (See DcmElement::checkVM() for a list of valid values.)
* Interpreted as cardinality (number of items) for sequence attributes.
* @param type Value type (valid value: "1", "1C", "2", something else)
* @param searchCond Optional flag indicating the status of a previous 'search' function call
* @param moduleName Optional module name to be printed (default: "IOD" if NULL)
* @return EC_Normal if element value is correct, error otherwise
*/
static OFCondition checkElementValue(const DcmElement &delem,
const OFString &vm,
const OFString &type,
const OFCondition &searchCond = EC_Normal,
const char *moduleName = NULL);
/** Get string value from element
* @param delem DICOM element from which the string value should be retrieved
* @param stringValue Reference to variable in which the result should be stored.
* (This parameter is automatically cleared if an error occurs.)
* @param pos Index of the value to get (0..vm-1), -1 for all components
* @return EC_Normal if successful, an error code otherwise
*/
static OFCondition getStringValueFromElement(const DcmElement &delem,
OFString &stringValue,
const signed long pos);
/** Get string value from item
* @param key The tag key of the attribute whose value should be retrieved
* @param item The item to search the attribute in
* @param result Reference to variable in which the result should be stored.
* (This parameter is automatically cleared if an error occurs.)
* @param pos Index of the value to get (0..vm-1), -1 for all components
* @return EC_Normal if successful, an error code otherwise
*/
static OFCondition getStringValueFromItem(const DcmTagKey& key,
DcmItem& item,
OFString& result,
const signed long& pos);
/** Get Float64 value from item
* @param key The tag key of the attribute whose value should be retrieved
* @param item The item to search the attribute in
* @param result Reference to variable in which the result should be stored.
* @param pos Index of the value to get (0..vm-1)
* @return EC_Normal if successful, an error code otherwise
*/
static OFCondition getFloat64ValueFromItem(const DcmTagKey& key,
DcmItem& item,
Float64& result,
const unsigned long& pos);
/** Get Float64 values from item
* @param key The tag key of the attribute whose value should be retrieved
* @param item The item to search the attribute in
* @param result Reference to variable in which the result should be stored.
* @return EC_Normal if successful, an error code otherwise
*/
static OFCondition getFloat64ValuesFromItem(const DcmTagKey& key,
DcmItem& item,
OFVector<Float64>& result);
/** Get Float64 value from element
* @param delem The element whose value should be retrieved
* @param result Reference to variable in which the result should be stored.
* @param pos Index of the value to get (0..vm-1)
* @return EC_Normal if successful, an error code otherwise
*/
static OFCondition getFloat64ValueFromElement(const DcmElement &delem,
Float64& result,
const unsigned long pos);
/** Get Float64 values from element
* @param delem The element to get the value from
* @param result Reference to variable in which the result should be stored.
* @return EC_Normal if successful, an error code otherwise
*/
static OFCondition getFloat64ValuesFromElement(const DcmElement &delem,
OFVector<Float64>& result);
/** Set a DcmElement's content from Uint16 vector
* @param delem DICOM element to be filled
* @param values Vector use as a source for the values
* @param vm Value multiplicity (according to the data dictionary) to be
* checked for. (See DcmElement::checkVM() for a list of
* valid values.)
* @param check If OFTrue, then it is checked whether number of values
* conforms to the value provided by the vm parameter.
* @return Dtatus, EC_Normal if successful, an error code otherwise
*/
static OFCondition setUint16ValuesOnElement(DcmElement &delem,
const OFVector<Uint16>& values,
const OFString& vm,
const OFBool check);
/** Get Uint16 values from DcmElement
* @param delem DICOM element to read from
* @param values Vector used as destination for the values
* @return Status EC_Normal if successful, an error code otherwise
*/
static OFCondition getUint16ValuesFromElement(DcmElement &delem,
OFVector<Uint16>& values);
/** Returns single item from given sequence. Returns error number of items
* is 0 or more than 1.
* @param seq Sequence to read from.
* @param item The resulting item, NULL in case of error
* @param checkKey If given it is checked whether the given seq parameter
* matches the tag key provided in the checkKey parameter. If
* not, an error is returned.
* @return Status EC_Normal if successful, an error code otherwise
*/
static OFCondition getAndCheckSingleItem(DcmSequenceOfItems& seq,
DcmItem*& item,
const DcmTagKey& checkKey = DCM_UndefinedTagKey);
/** Copies Uint8 values from given element to destination container
* (must support push_back() method).
* @param elem the element to read from. If NULL, an error is returned.
* @param destination the container to store the Uint8 values to
* @return EC_Normal if successful, an error code otherwise
*/
template <class Container>
static OFCondition copyFromUint8Array(DcmElement* elem,
Container& destination)
{
if (elem == NULL)
return EC_IllegalParameter;
size_t vm = elem->getVM();
OFCondition result;
for (size_t count = 0; (count < vm) && result.good(); count ++)
{
Uint8 value;
result = elem->getUint8(value, count);
if ( result.good() )
{
destination.push_back(value);
}
}
if ( result.bad() )
{
destination.clear();
}
return result;
}
/** Check whether SOP class UID matches the expected value
* @param item Item to read from. NULL value causes error return value.
* @param desiredSOPClass The value that is expected
* @param valueFound The value actually found (empty if no value could
* be retrieved)
* @return EC_Normal if value could be found and equals expected value,
* EC_TagNotFound if SOP Class UID is not found in dataset,
* EC_InvalidValue if SOP class differs from expected value.
*/
static OFCondition checkSOPClass(DcmItem* item,
const OFString& desiredSOPClass,
OFString& valueFound);
/** Check whether given tag is a sequence tag
* @param key the tag key to be checked
* @param privateCreator The private creator to check for if tag is private
* @return OFTrue if given tag is a sequence tag, OFFalse otherwise
*/
static OFBool isSequenceTag(const DcmTagKey& key,
const OFString& privateCreator = "");
/** Reads items from DICOM sequence into container by creating a dedicated
* container item for each DICOM item. The container must support
* push_back(T) function and the container's element type T must support
* the read(DcmItem*) function.
* @param source The source sequence to read items from
* @param seqKey The sequence's tag key that is expected
* @param destination The destination container to read into
* @param cardinality Expected number of items.
* See DcmElement::checkVM() for a list of valid values.
* @param type The sequence type as noted in part 3 of the DICOM standard,
* i.e.\ "1,1C,2,2C or 3".
* @param module Name of the module/macro this sequence is contained in. Used
* for error messages and can also be left empty.
* @return EC_Normal if successful, an error code otherwise
*/
template <class Container>
static OFCondition readSubSequence(DcmSequenceOfItems* source,
const DcmTagKey& seqKey,
Container& destination,
const OFString& cardinality,
const OFString& type,
const OFString& module)
{
OFCondition result;
OFCondition exists = EC_Normal;
if (!source)
exists = EC_TagNotFound;
/* Check sequence and report errors as warnings, read anyway */
checkElementValue(source, seqKey, cardinality, type, exists, module.c_str());
if (source)
{
DcmItem *item = OFstatic_cast(DcmItem*, source->nextInContainer(NULL));
size_t count = 0;
while (item != NULL)
{
if (item != NULL)
{
// define the element type
typedef typename OFremove_pointer<typename Container::value_type>::type Element;
Element *newElem = new Element();
// read into container item (clears old data first)
if ( newElem != NULL)
{
result = (newElem)->read(*item, OFFalse /* no need to delete anything */);
if ( result.good() )
{
destination.push_back(newElem);
}
else
{
delete newElem;
DCMIOD_WARN("Could not read item #" << count << " from " << DcmTag(source->getTag()).getTagName() << " (skipping item): " << result.text());
}
}
else
{
DCMIOD_ERROR("Could not store data from item #" << count << " from " << DcmTag(source->getTag()).getTagName() << " (skipping item): Memory exhausted?");
}
}
else
{
DCMIOD_WARN("Could not get item #" << count << " from " << DcmTag(source->getTag()).getTagName() << " (malformed data or internal error), skipping item");
}
item = OFstatic_cast(DcmItem*, source->nextInContainer(item));
count++;
}
}
else
{
result = EC_IllegalParameter;
}
return result;
}
/** Reads DICOM sequence into container by creating a dedicated container element
* for each DICOM item. The container must support push_back(T) function
* and the container's element type T must support the read(DcmItem*) function.
* @param source The source DICOM item read the sequence from
* @param seqKey The tag key of the sequence to be read
* @param destination The destination container to read into
* @param cardinality Expected number of items.
* See DcmElement::checkVM() for a list of valid values.
* @param type The sequence type as noted in part 3 of the DICOM standard,
* i.e.\ "1,1C,2,2C or 3".
* @param module Nname of the module/macro this sequence is contained in. Used
* for error messages and can also be left empty.
* @return status EC_Normal if successful, an error code otherwise
*/
template <class Container>
static OFCondition readSubSequence(DcmItem& source,
const DcmTagKey& seqKey,
Container& destination,
const OFString& cardinality,
const OFString& type,
const OFString& module)
{
OFCondition result;
DcmSequenceOfItems *seq = NULL;
/* Get sequence and read it */
source.findAndGetSequence(seqKey, seq);
result = readSubSequence(seq, seqKey, destination, cardinality, type, module );
return result;
}
/** Reads DICOM sequence into container by creating a dedicated container
* element for each DICOM item. The container must support the
* push_back(T) function and the container's element type T must support
* the read(DcmItem*) function.
* @param source The source DICOM item read the sequence from
* @param seqKey The tag key of the sequence to be read
* @param destination The destination container to read into
* @param rule The rule for reading this sequence. If NULL, an error is returned.
* @return EC_Normal if successful, an error code otherwise
*/
template <class Container>
static OFCondition readSubSequence(DcmItem& source,
const DcmTagKey& seqKey,
Container& destination,
IODRule *rule)
{
if (rule == NULL)
{
DCMIOD_ERROR("Cannot read sequence " << seqKey << " (no rule supplied)");
return IOD_EC_NoSuchRule;
}
return readSubSequence<Container>(source, seqKey, destination, rule->getVM(), rule->getType(), rule->getModule());
}
/** Reads single DICOM item from a specific sequence into a destination class.
* The container must support the read(DcmItem) function. If a single item
* (i.e.\ the first item of the specified sequence) cannot be read, an error
* is returned.
* @param source The source DICOM item read the sequence from
* @param seqKey The tag key of the sequence to be read
* @param destination The destination container to read into
* See DcmElement::checkVM() for a list of valid values.
* @param type The sequence type as noted in part 3 of the DICOM standard,
* i.e.\ "1,1C,2,2C or 3".
* @param module Name of the module/macro this sequence is contained in.
* Used for error messages and can also be left empty.
* @return EC_Normal if successful, an error code otherwise
*/
template <class Container>
static OFCondition readSingleItem(DcmItem& source,
const DcmTagKey& seqKey,
Container& destination,
const OFString& type,
const OFString& module)
{
OFCondition result;
/* Check sequence, reports cardinality and type errors as warnings */
checkSubSequence(result, source, seqKey, "1", type, module);
/* Try to read sequence into internal data (ignore errors as much as possible) */
DcmItem* item = NULL;
result = source.findAndGetSequenceItem(seqKey, item, 0);
if (item != NULL)
{
// read into Container (clears old data first)
result = destination.read(*item, OFTrue /* clear old data */);
}
return result;
}
/** Reads single DICOM item from a specific sequence into a destination class.
* The container must support the read(DcmItem) function. If a single item
* (i.e.\ the first item of the specified sequence) cannot be read, an error
* is returned.
* @param source The source DICOM item read the sequence from
* @param seqKey The tag key of the sequence to be read
* @param destination The destination container to read into
* See DcmElement::checkVM() for a list of valid values.
* @param rule The rule (for the sequence) used for reading.
* @return EC_Normal if successful, an error code otherwise. If the rule
* equlas NULL, then nothing is read at all and an error is returned
* (IOD_EC_NoSuchRule), but no error is reported to the logger.
*/
template <class Container>
static OFCondition readSingleItem(DcmItem& source,
const DcmTagKey& seqKey,
Container& destination,
IODRule *rule = NULL)
{
if (rule == NULL)
{
DCMIOD_DEBUG("Will not read sequence " << seqKey << ": No rule supplied");
return IOD_EC_NoSuchRule;
}
return readSingleItem(source, seqKey, destination, rule->getType(), rule->getModule());
}
/** Write given container into a DICOM sequence that is created within the
* given item. The sequence is created from scratch, i.e.\ any old sequence
* (and its items) will be overwritten.
* @param result If writing is successful, result will contain EC_Normal.
* Otherwise an error code is set.
* @param seqKey The tag key of the sequence to be write
* @param source The source container to read from. Must support the
* write(DcmItem*) function.
* @param destination The DICOM destination item to write the sequence to
* See DcmElement::checkVM() for a list of valid values.
* @param cardinality Expected number of items (i.e.\ expected number of
* elements in source container). See DcmElement::checkVM() for a
* list of valid values.
* @param type The sequence type as noted in part 3 of the DICOM standard,
* i.e.\ "1,1C,2,2C or 3".
* @param module Name of the module/macro this sequence is contained in. Used
* for error messages and can also be left empty.
* @return EC_Normal if successful, an error code otherwise
*/
template <class Container>
static void writeSubSequence(OFCondition& result,
const DcmTagKey& seqKey,
Container& source,
DcmItem& destination,
const OFString& cardinality,
const OFString& type,
const OFString& module)
{
if ( result.good() )
{
// Delete old data
destination.findAndDeleteElement(seqKey);
// If we have meaningful data, write it
OFBool haveData = source.size() > 0;
if (haveData)
{
destination.insertEmptyElement(seqKey);
Uint32 count = 0;
typename Container::iterator it = source.begin();
while ( it != source.end() && result.good() )
{
if ( (*it) != NULL)
{
DcmItem *localItem = NULL;
// If item is NULL result.bad() is always true.
result = destination.findOrCreateSequenceItem(seqKey, localItem, -2 /* append new */);
if ( result.good() )
{
result = (*it)->write(*localItem);
if ( result.bad() )
{
destination.findAndDeleteSequenceItem(seqKey, -1 /* last */);
}
}
else
{
DCMIOD_ERROR("Could not insert item data #" << count << " of " << DcmTag(seqKey).getTagName() << " (internal error), ignoring");
}
}
else
{
DCMIOD_ERROR("Found uninitialized container item (NULL value) for sequence " << DcmTag(seqKey).getTagName() << " (internal error, skipping)");
}
count++;
it++;
}
}
// If we do not have data, create empty sequence if type 2
else if (type == "2")
{
destination.insertEmptyElement(seqKey);
}
// Check result
checkSubSequence(result, destination, seqKey, cardinality, type, module);
// Clean up if we did not have success */
if (result.bad())
{
destination.findAndDeleteElement(seqKey);
}
}
}
/** Write given container into a DICOM sequence that is created within the given item.
* The container. The sequence is created from scratch, i.e.\ any old sequence
* (and its items) will be overwritten.
* @param result If writing is successful, result will contain EC_Normal. Otherwise
* an error code is set.
* @param seqKey The tag key of the sequence to be write
* @param source The source container to read from. Must support the write(DcmItem*)
* function.
* @param destination The DICOM destination item to write the sequence to
* @param rule Rule describing the requirements for this sequence. If NULL
* an error is returned (IOD_EC_NoSuchRule), but no error error is reported
* to the logger.
* @return EC_Normal if successful, an error code otherwise
*/
template <class Container>
static void writeSubSequence(OFCondition& result,
const DcmTagKey& seqKey,
Container& source,
DcmItem& destination,
IODRule* rule)
{
if ( result.good() )
{
if (rule == NULL)
{
DCMIOD_DEBUG("Will not write sequence " << seqKey << ": No rule supplied");
result = IOD_EC_NoSuchRule;
}
else
{
writeSubSequence<Container>(result, rule->getTagKey(), source, destination, rule->getVM(), rule->getType(), rule->getModule());
}
}
}
/** Writes given container into a DICOM item of a specific sequence. The
* sequence is created from scratch so that any old information will be lost.
* @param result If writing is successful, result will contain EC_Normal.
* Otherwise an error code is set.
* @param seqKey The tag key of the sequence to be written
* @param source The source container to read from. Must support the write(DcmItem*)
* function.
* @param destination The DICOM item that should hold the sequence
* (with a single item) in the end.
* @param type The sequence type as noted in part 3 of the DICOM standard,
* i.e.\ "1,1C,2,2C or 3".
* @param module Name of the module/macro this sequence is contained in.
* Used for error messages and can also be left empty.
* @return EC_Normal if successful, an error code otherwise
*/
template <class Container>
static void writeSingleItem(OFCondition& result,
const DcmTagKey& seqKey,
Container& source,
DcmItem& destination,
const OFString& type,
const OFString& module)
{
if ( result.good() )
{
// Delete old data
destination.findAndDeleteElement(seqKey);
/* If we have data, write it */
OFCondition haveData = source.check(OFTrue /* Be quiet */);
if (haveData.good())
{
DcmItem *localItem = NULL;
// If item is NULL result.bad() is always true.
result = destination.findOrCreateSequenceItem(seqKey, localItem, 0);
if ( result.good() )
{
result = source.write(*localItem);
// It can happen that check() returns OK but no elements have to be
// written at all (e.g.\ if it contains only type 3 attributes). In that
// case a sequence with a single empty item is written which must be removed
// afterwards.
if (result.good() && (localItem->card() == 0) )
{
destination.findAndDeleteElement(seqKey);
}
}
}
/* If we do not have data, insert empty for type 2 */
else if (type == "2")
{
destination.insertEmptyElement(seqKey);
}
else if (type == "1C")
{
DCMIOD_TRACE("Skipping type 1C sequence " << seqKey << ": No data or incomplete data available");
}
/* Check outcome */
checkSubSequence(result, destination, seqKey, "1", type, module);
}
}
/** Writes given container into a DICOM item of a specific sequence.
* The sequence is created from scratch so that any old information
* will be lost.
* @param result If writing is successful, result will contain EC_Normal.
* Otherwise an error code is set.
* @param seqKey The tag key of the sequence to be written
* @param source The source container to read from. Must support the
* write(DcmItem*) function.
* @param destination The DICOM item that should hold the sequence
* (with a single item) in the end.
* @param rule The rule for writing the given sequence
* @return EC_Normal if successful, an error code otherwise
*/
template <class Container>
static void writeSingleItem(OFCondition& result,
const DcmTagKey& seqKey,
Container& source,
DcmItem& destination,
IODRule *rule)
{
if (result.good())
{
if (rule == NULL)
{
DCMIOD_ERROR("Cannot write sequence " << seqKey << " (no rule supplied)");
result = EC_CannotCheck;
}
writeSingleItem(result, seqKey, source, destination, rule->getType(), rule->getModule());
}
}
/** Check whether a given sequence exists in a given item and whether it conforms to
* to its requirement type
* @param result If sequence is valid, result is set to EC_Normal, otherwise
* to an error.
* @param surroundingItem The item that should contain the given sequence.
* @param seqKey The sequence to look for
* @param cardinality Expected number of items (i.e.\ expected number of
* elements in source container). See DcmElement::checkVM() for a
* list of valid values.
* @param type The sequence type as noted in part 3 of the DICOM standard,
* i.e.\ "1,1C,2,2C or 3".
* @param module Name of the module/macro this sequence is contained in.
* Used for error messages and can also be left empty.
* @return EC_Normal if successful, an error code otherwise
*/
static void checkSubSequence(OFCondition& result,
DcmItem& surroundingItem,
const DcmTagKey& seqKey,
const OFString& cardinality,
const OFString& type,
const OFString& module);
/** Deletes all elements from given container and calls "delete" on each
* of them to clear memory.
* @param container The container that should be cleared. Must contain
* pointers to objects that are allocated on the heap.
*/
template<class Container>
static void freeContainer(Container& container)
{
typename Container::iterator it = container.begin();
while (it != container.end())
{
delete *it;
it++;
}
container.clear();
}
/** Clones and copies all elements from source to destination container by
* copy constructing all elements.
* @param src The container that should be copied. Must contain pointers
* to objects that are allocated on the heap, and that are
* copy-constructible
* @param dst The container to copy the cloned elements to.
*/
template<class Container>
static void copyContainer(const Container& src, Container& dst)
{
typedef typename OFremove_pointer<typename Container::value_type>::type Element;
typename Container::const_iterator it = src.begin();
while (it != src.end())
{
if (*it != NULL)
{
Element *elem = new Element(**it);
dst.push_back ( elem );
}
else
{
DCMIOD_ERROR("Could not copy element while copying container: Element is NULL, skipping");
}
it++;
}
}
/** Function that takes a string representation of a tag key and
* converts it to a tag key instance if possible
* @param keyString String of the format "(gggg,eeee)"
* @return The tag key if it could be parsed, DCM_UndefinedTagKey is returned
* instead (0xffff,0xffff)
*/
static const DcmTagKey parseTagKey(const OFString& keyString);
/** Decompress given dataset if possible. Decompression codecs have to be
* registered beforehand.
* @param dset The dataset to decompress
* @return EC_Normal if decompression works, error otherwise
*/
static OFCondition decompress(DcmDataset& dset);
/** Create new Unique Identifier (UID)
* @param level 0: instance level, 1: Series level, >=2: Study level.
* Calling always with level=0 is not an error but will result
* in unique values, too.
* @return The UID created.
*/
static OFString createUID(const Uint8 level = 0);
private:
// We only have static functions so we do not need an instance of
// this class so far.
/** Undefined default constructor (only static functions)
*/
DcmIODUtil();
/** Undefined destructor
*/
~DcmIODUtil();
};
#endif // IODUTIL_H