/* * * Copyright (C) 2000-2019, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: dcmsr * * Author: Joerg Riesmeier * * Purpose: * classes: DSRTemporalCoordinatesValue * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/dcmsr/dsrtcovl.h" #include "dcmtk/dcmsr/dsrxmld.h" #include "dcmtk/dcmdata/dcdeftag.h" DSRTemporalCoordinatesValue::DSRTemporalCoordinatesValue() : TemporalRangeType(DSRTypes::TRT_invalid), SamplePositionList(), TimeOffsetList(), DateTimeList() { } DSRTemporalCoordinatesValue::DSRTemporalCoordinatesValue(const DSRTypes::E_TemporalRangeType temporalRangeType) : TemporalRangeType(temporalRangeType), SamplePositionList(), TimeOffsetList(), DateTimeList() { } DSRTemporalCoordinatesValue::DSRTemporalCoordinatesValue(const DSRTemporalCoordinatesValue &coordinatesValue) : TemporalRangeType(coordinatesValue.TemporalRangeType), SamplePositionList(coordinatesValue.SamplePositionList), TimeOffsetList(coordinatesValue.TimeOffsetList), DateTimeList(coordinatesValue.DateTimeList) { } DSRTemporalCoordinatesValue::~DSRTemporalCoordinatesValue() { } DSRTemporalCoordinatesValue &DSRTemporalCoordinatesValue::operator=(const DSRTemporalCoordinatesValue &coordinatesValue) { TemporalRangeType = coordinatesValue.TemporalRangeType; SamplePositionList = coordinatesValue.SamplePositionList; TimeOffsetList = coordinatesValue.TimeOffsetList; DateTimeList = coordinatesValue.DateTimeList; return *this; } OFBool DSRTemporalCoordinatesValue::operator==(const DSRTemporalCoordinatesValue &coordinatesValue) const { return (TemporalRangeType == coordinatesValue.TemporalRangeType) && (SamplePositionList == coordinatesValue.SamplePositionList) && (TimeOffsetList == coordinatesValue.TimeOffsetList) && (DateTimeList == coordinatesValue.DateTimeList); } OFBool DSRTemporalCoordinatesValue::operator!=(const DSRTemporalCoordinatesValue &coordinatesValue) const { return (TemporalRangeType != coordinatesValue.TemporalRangeType) || (SamplePositionList != coordinatesValue.SamplePositionList) || (TimeOffsetList != coordinatesValue.TimeOffsetList) || (DateTimeList != coordinatesValue.DateTimeList); } void DSRTemporalCoordinatesValue::clear() { TemporalRangeType = DSRTypes::TRT_invalid; SamplePositionList.clear(); TimeOffsetList.clear(); DateTimeList.clear(); } OFBool DSRTemporalCoordinatesValue::isValid() const { return checkData(TemporalRangeType, SamplePositionList, TimeOffsetList, DateTimeList).good(); } OFBool DSRTemporalCoordinatesValue::isShort(const size_t flags) const { return (SamplePositionList.isEmpty() && TimeOffsetList.isEmpty() && DateTimeList.isEmpty()) || !(flags & DSRTypes::HF_renderFullData); } OFCondition DSRTemporalCoordinatesValue::print(STD_NAMESPACE ostream &stream, const size_t flags) const { /* TemporalRangeType */ stream << "(" << DSRTypes::temporalRangeTypeToEnumeratedValue(TemporalRangeType); /* print data */ stream << ","; /* print only one list */ if (!SamplePositionList.isEmpty()) SamplePositionList.print(stream, flags); else if (!TimeOffsetList.isEmpty()) TimeOffsetList.print(stream, flags); else DateTimeList.print(stream, flags); stream << ")"; return EC_Normal; } OFCondition DSRTemporalCoordinatesValue::readXML(const DSRXMLDocument &doc, DSRXMLCursor cursor, const size_t /*flags*/) { OFCondition result = SR_EC_CorruptedXMLStructure; if (cursor.valid()) { /* graphic data (required) */ cursor = doc.getNamedChildNode(cursor, "data"); if (cursor.valid()) { OFString tmpString, typeString; /* read 'type' and check validity */ doc.getStringFromAttribute(cursor, typeString, "type"); if (typeString == "SAMPLE POSITION") { /* put value to the sample position list */ result = SamplePositionList.putString(doc.getStringFromNodeContent(cursor, tmpString).c_str()); } else if (typeString == "TIME OFFSET") { /* put value to the time offset list */ result = TimeOffsetList.putString(doc.getStringFromNodeContent(cursor, tmpString).c_str()); } else if (typeString == "DATETIME") { /* put value to the date/time list (tbd: convert from ISO 8601 format?) */ result = DateTimeList.putString(doc.getStringFromNodeContent(cursor, tmpString).c_str()); } else { DSRTypes::printUnknownValueWarningMessage("TCOORD data type", typeString.c_str()); result = SR_EC_InvalidValue; } } } return result; } OFCondition DSRTemporalCoordinatesValue::writeXML(STD_NAMESPACE ostream &stream, const size_t flags) const { /* TemporalRangeType is written in TreeNode class */ if ((flags & DSRTypes::XF_writeEmptyTags) || !SamplePositionList.isEmpty() || !TimeOffsetList.isEmpty() || !DateTimeList.isEmpty()) { stream << ""; SamplePositionList.print(stream); } else if (!TimeOffsetList.isEmpty()) { stream << "TIME OFFSET\">"; TimeOffsetList.print(stream); } else { /* tbd: convert output to ISO 8601 format? */ stream << "DATETIME\">"; DateTimeList.print(stream); } stream << "" << OFendl; } return EC_Normal; } OFCondition DSRTemporalCoordinatesValue::read(DcmItem &dataset, const size_t flags) { /* read TemporalRangeType */ OFString tmpString; OFCondition result = DSRTypes::getAndCheckStringValueFromDataset(dataset, DCM_TemporalRangeType, tmpString, "1", "1", "TCOORD content item"); if (result.good()) { TemporalRangeType = DSRTypes::enumeratedValueToTemporalRangeType(tmpString); /* check TemporalRangeType */ if (TemporalRangeType == DSRTypes::TRT_invalid) DSRTypes::printUnknownValueWarningMessage("TemporalRangeType", tmpString.c_str()); /* first read data (all three lists) */ SamplePositionList.read(dataset, flags); TimeOffsetList.read(dataset, flags); DateTimeList.read(dataset, flags); /* then check data and report warnings if any */ checkData(TemporalRangeType, SamplePositionList, TimeOffsetList, DateTimeList, OFTrue /*reportWarnings*/); } return result; } OFCondition DSRTemporalCoordinatesValue::write(DcmItem &dataset) const { /* write TemporalRangeType */ OFCondition result = DSRTypes::putStringValueToDataset(dataset, DCM_TemporalRangeType, DSRTypes::temporalRangeTypeToEnumeratedValue(TemporalRangeType)); if (result.good()) { /* write data (only one list) */ if (!SamplePositionList.isEmpty()) result = SamplePositionList.write(dataset); else if (!TimeOffsetList.isEmpty()) result = TimeOffsetList.write(dataset); else result = DateTimeList.write(dataset); } /* check data and report warnings if any */ checkData(TemporalRangeType, SamplePositionList, TimeOffsetList, DateTimeList, OFTrue /*reportWarnings*/); return result; } OFCondition DSRTemporalCoordinatesValue::renderHTML(STD_NAMESPACE ostream &docStream, STD_NAMESPACE ostream &annexStream, size_t &annexNumber, const size_t flags) const { /* render TemporalRangeType */ docStream << DSRTypes::temporalRangeTypeToReadableName(TemporalRangeType); /* render data */ if (!isShort(flags)) { const char *lineBreak = (flags & DSRTypes::HF_renderSectionTitlesInline) ? " " : (flags & DSRTypes::HF_XHTML11Compatibility) ? "
" : "
"; if (flags & DSRTypes::HF_currentlyInsideAnnex) { docStream << OFendl << "

" << OFendl; /* render data list (= print)*/ if (!SamplePositionList.isEmpty()) { docStream << "Reference Sample Positions:" << lineBreak; SamplePositionList.print(docStream); } else if (!TimeOffsetList.isEmpty()) { docStream << "Referenced Time Offsets:" << lineBreak; TimeOffsetList.print(docStream); } else { docStream << "Referenced Date/Time:" << lineBreak; DateTimeList.print(docStream); } docStream << "

"; } else { DSRTypes::createHTMLAnnexEntry(docStream, annexStream, "for more details see", annexNumber, flags); annexStream << "

" << OFendl; /* render data list (= print)*/ if (!SamplePositionList.isEmpty()) { annexStream << "Reference Sample Positions:" << lineBreak; SamplePositionList.print(annexStream); } else if (!TimeOffsetList.isEmpty()) { annexStream << "Referenced Time Offsets:" << lineBreak; TimeOffsetList.print(annexStream); } else { annexStream << "Referenced Date/Time:" << lineBreak; DateTimeList.print(annexStream); } annexStream << "

" << OFendl; } } return EC_Normal; } OFCondition DSRTemporalCoordinatesValue::setValue(const DSRTemporalCoordinatesValue &coordinatesValue, const OFBool check) { OFCondition result = EC_Normal; if (check) { /* check whether the passed value is valid */ result = checkData(coordinatesValue.TemporalRangeType, coordinatesValue.SamplePositionList, coordinatesValue.TimeOffsetList, coordinatesValue.DateTimeList); } else { /* make sure that the mandatory values are non-empty/invalid */ if ((coordinatesValue.TemporalRangeType == DSRTypes::TRT_invalid) || (coordinatesValue.SamplePositionList.isEmpty() && coordinatesValue.TimeOffsetList.isEmpty() && coordinatesValue.DateTimeList.isEmpty())) { result = EC_IllegalParameter; } } if (result.good()) { TemporalRangeType = coordinatesValue.TemporalRangeType; SamplePositionList = coordinatesValue.SamplePositionList; TimeOffsetList = coordinatesValue.TimeOffsetList; DateTimeList = coordinatesValue.DateTimeList; } return result; } OFCondition DSRTemporalCoordinatesValue::getValue(DSRTemporalCoordinatesValue &coordinatesValue) const { coordinatesValue = *this; return EC_Normal; } OFCondition DSRTemporalCoordinatesValue::setTemporalRangeType(const DSRTypes::E_TemporalRangeType temporalRangeType, const OFBool /*check*/) { OFCondition result = EC_IllegalParameter; /* check whether the passed value is valid */ if (temporalRangeType != DSRTypes::TRT_invalid) { TemporalRangeType = temporalRangeType; result = EC_Normal; } return result; } // helper macro to avoid annoying check of boolean flag #define REPORT_WARNING(msg) { if (reportWarnings) DCMSR_WARN(msg); } OFCondition DSRTemporalCoordinatesValue::checkData(const DSRTypes::E_TemporalRangeType temporalRangeType, const DSRReferencedSamplePositionList &samplePositionList, const DSRReferencedTimeOffsetList &timeOffsetList, const DSRReferencedDateTimeList &dateTimeList, const OFBool reportWarnings) const { OFCondition result = EC_Normal; if (temporalRangeType == DSRTypes::TRT_invalid) REPORT_WARNING("Invalid Temporal Range Type for TCOORD content item") const OFBool list1 = !samplePositionList.isEmpty(); const OFBool list2 = !timeOffsetList.isEmpty(); const OFBool list3 = !dateTimeList.isEmpty(); if (list1 && list2 && list3) REPORT_WARNING("Referenced Sample Positions/Time Offsets/DateTime present in TCOORD content item") else if (list1 && list2) REPORT_WARNING("Referenced Sample Positions/Time Offsets present in TCOORD content item") else if (list1 && list3) REPORT_WARNING("Referenced Sample Positions/DateTime present in TCOORD content item") else if (list2 && list3) REPORT_WARNING("Referenced Time Offsets/DateTime present in TCOORD content item") else if (!list1 && !list2 && !list3) { REPORT_WARNING("Referenced Sample Positions/Time Offsets/DateTime empty in TCOORD content item") /* invalid: all lists are empty (type 1C condition violated) */ result = SR_EC_InvalidValue; } return result; }