/* * * Copyright (C) 2000-2017, 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: DSRNumericMeasurementValue * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/dcmsr/dsrtypes.h" #include "dcmtk/dcmsr/dsrnumvl.h" #include "dcmtk/dcmsr/dsrxmld.h" #include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmdata/dcvrds.h" DSRNumericMeasurementValue::DSRNumericMeasurementValue() : NumericValue(), MeasurementUnit(), ValueQualifier(), FloatingPointValue(DCM_FloatingPointValue), RationalNumeratorValue(DCM_RationalNumeratorValue), RationalDenominatorValue(DCM_RationalDenominatorValue) { } DSRNumericMeasurementValue::DSRNumericMeasurementValue(const OFString &numericValue, const DSRCodedEntryValue &measurementUnit, const OFBool check) : NumericValue(), MeasurementUnit(), ValueQualifier(), FloatingPointValue(DCM_FloatingPointValue), RationalNumeratorValue(DCM_RationalNumeratorValue), RationalDenominatorValue(DCM_RationalDenominatorValue) { /* use the set method for checking purposes */ setValue(numericValue, measurementUnit, check); } DSRNumericMeasurementValue::DSRNumericMeasurementValue(const DSRCodedEntryValue &valueQualifier, const OFBool check) : NumericValue(), MeasurementUnit(), ValueQualifier(), FloatingPointValue(DCM_FloatingPointValue), RationalNumeratorValue(DCM_RationalNumeratorValue), RationalDenominatorValue(DCM_RationalDenominatorValue) { /* use the set method for checking purposes */ setValue(valueQualifier, check); } DSRNumericMeasurementValue::DSRNumericMeasurementValue(const OFString &numericValue, const DSRCodedEntryValue &measurementUnit, const DSRCodedEntryValue &valueQualifier, const OFBool check) : NumericValue(), MeasurementUnit(), ValueQualifier(), FloatingPointValue(DCM_FloatingPointValue), RationalNumeratorValue(DCM_RationalNumeratorValue), RationalDenominatorValue(DCM_RationalDenominatorValue) { /* use the set method for checking purposes */ setValue(numericValue, measurementUnit, valueQualifier, check); } DSRNumericMeasurementValue::DSRNumericMeasurementValue(const DSRNumericMeasurementValue &numericMeasurement) : NumericValue(numericMeasurement.NumericValue), MeasurementUnit(numericMeasurement.MeasurementUnit), ValueQualifier(numericMeasurement.ValueQualifier), FloatingPointValue(numericMeasurement.FloatingPointValue), RationalNumeratorValue(numericMeasurement.RationalNumeratorValue), RationalDenominatorValue(numericMeasurement.RationalDenominatorValue) { /* do not check since this would be unexpected to the user */ } DSRNumericMeasurementValue::~DSRNumericMeasurementValue() { } DSRNumericMeasurementValue &DSRNumericMeasurementValue::operator=(const DSRNumericMeasurementValue &numericMeasurement) { /* do not check since this would be unexpected to the user */ NumericValue = numericMeasurement.NumericValue; MeasurementUnit = numericMeasurement.MeasurementUnit; ValueQualifier = numericMeasurement.ValueQualifier; FloatingPointValue = numericMeasurement.FloatingPointValue; RationalNumeratorValue = numericMeasurement.RationalNumeratorValue; RationalDenominatorValue = numericMeasurement.RationalDenominatorValue; return *this; } OFBool DSRNumericMeasurementValue::operator==(const DSRNumericMeasurementValue &numericMeasurement) const { /* only the basic information is used for comparing the two values */ return (NumericValue == numericMeasurement.NumericValue) && (MeasurementUnit == numericMeasurement.MeasurementUnit) && (ValueQualifier == numericMeasurement.ValueQualifier); } OFBool DSRNumericMeasurementValue::operator!=(const DSRNumericMeasurementValue &numericMeasurement) const { /* only the basic information is used for comparing the two values */ return (NumericValue != numericMeasurement.NumericValue) || (MeasurementUnit != numericMeasurement.MeasurementUnit) || (ValueQualifier != numericMeasurement.ValueQualifier); } void DSRNumericMeasurementValue::clear() { NumericValue.clear(); MeasurementUnit.clear(); ValueQualifier.clear(); FloatingPointValue.clear(); RationalNumeratorValue.clear(); RationalDenominatorValue.clear(); } OFBool DSRNumericMeasurementValue::isValid() const { /* the MeasuredValueSequence can be empty (type 2) */ return (isEmpty() || (checkNumericValue(NumericValue).good() && checkMeasurementUnit(MeasurementUnit).good())) && checkNumericValueQualifier(ValueQualifier).good(); } OFBool DSRNumericMeasurementValue::isEmpty() const { /* the NumericValueQualifierCodeSequence is not checked */ return NumericValue.empty() && MeasurementUnit.isEmpty(); } OFBool DSRNumericMeasurementValue::isComplete() const { /* officially, the NumericValueQualifierCodeSequence is optional (type 3) */ return (!NumericValue.empty() && MeasurementUnit.isComplete()) || ValueQualifier.isComplete(); } OFCondition DSRNumericMeasurementValue::print(STD_NAMESPACE ostream &stream, const size_t flags) const { if (isEmpty()) { /* empty value */ stream << "empty"; /* check for optional numeric value qualifier */ if (!ValueQualifier.isEmpty()) { stream << " "; ValueQualifier.print(stream, OFTrue /*printCodeValue*/, flags); } } else { OFString printString; stream << "\"" << DSRTypes::convertToPrintString(NumericValue, printString) << "\" "; MeasurementUnit.print(stream, OFTrue /*printCodeValue*/, flags); } return EC_Normal; } OFCondition DSRNumericMeasurementValue::readXML(const DSRXMLDocument &doc, DSRXMLCursor cursor, const size_t flags) { OFCondition result = SR_EC_CorruptedXMLStructure; if (cursor.valid()) { cursor.gotoChild(); /* get "value" element (might be absent since "Measured Value Sequence" is type 2) */ if (!doc.getStringFromNodeContent(doc.getNamedNode(cursor, "value", OFFalse /*required*/), NumericValue).empty()) { /* get additional representations of the numeric value (if any) */ doc.getElementFromNodeContent(doc.getNamedNode(cursor, "float", OFFalse /*required*/), FloatingPointValue); const DSRXMLCursor childNode = doc.getNamedNode(cursor, "rational", OFFalse /*required*/).getChild(); if (childNode.valid()) { doc.getElementFromNodeContent(doc.getNamedNode(childNode, "numerator"), RationalNumeratorValue); doc.getElementFromNodeContent(doc.getNamedNode(childNode, "denominator"), RationalDenominatorValue); } /* get "unit" element (only if "value" present) */ result = MeasurementUnit.readXML(doc, doc.getNamedNode(cursor, "unit"), flags); } else result = EC_Normal; if (result.good()) { /* get "qualifier" element (optional, do not report if absent or erroneous) */ ValueQualifier.readXML(doc, doc.getNamedNode(cursor, "qualifier", OFFalse /*required*/), flags); } if (!isValid()) result = SR_EC_InvalidValue; } return result; } OFCondition DSRNumericMeasurementValue::writeXML(STD_NAMESPACE ostream &stream, const size_t flags) const { /* write numeric value */ DSRTypes::writeStringValueToXML(stream, NumericValue, "value", (flags & DSRTypes::XF_writeEmptyTags) > 0); /* write floating point representation */ Float64 floatValue; const OFBool hasFloating = getFloatingPointRepresentation(floatValue).good(); if (hasFloating || (flags & DSRTypes::XF_writeEmptyTags)) { stream << ""; if (hasFloating) { /* increase default precision */ const STD_NAMESPACE streamsize oldPrecision = stream.precision(8); stream << floatValue; /* reset i/o manipulators */ stream.precision(oldPrecision); } stream << "" << OFendl; } /* write rational representation */ Sint32 numeratorValue; Uint32 denominatorValue; const OFBool hasRational = getRationalRepresentation(numeratorValue, denominatorValue).good(); if (hasRational || (flags & DSRTypes::XF_writeEmptyTags)) { stream << "" << OFendl; if (hasRational) { stream << "" << numeratorValue << "" << OFendl; stream << "" << denominatorValue << "" << OFendl; } stream << "" << OFendl; } /* write measurement unit */ if (!MeasurementUnit.isEmpty() || (flags & DSRTypes::XF_writeEmptyTags)) { if (flags & DSRTypes::XF_codeComponentsAsAttribute) stream << "" is closed in the next writeXML() routine else stream << "" << OFendl; MeasurementUnit.writeXML(stream, flags); stream << "" << OFendl; } /* write value qualifier */ if (!ValueQualifier.isEmpty() || (flags & DSRTypes::XF_writeEmptyTags)) { if (flags & DSRTypes::XF_codeComponentsAsAttribute) stream << "" is closed in the next writeXML() routine else stream << "" << OFendl; ValueQualifier.writeXML(stream, flags); stream << "" << OFendl; } return EC_Normal; } OFCondition DSRNumericMeasurementValue::readItem(DcmItem &dataset, const size_t flags) { const OFBool acceptViolation = (flags & DSRTypes::RF_acceptInvalidContentItemValue) > 0; /* read NumericValue */ OFCondition result = DSRTypes::getAndCheckStringValueFromDataset(dataset, DCM_NumericValue, NumericValue, "1", "1", "MeasuredValueSequence", acceptViolation); if (result.good()) { /* read some optional attributes */ DSRTypes::getAndCheckElementFromDataset(dataset, FloatingPointValue, "1", "1C", "MeasuredValueSequence"); if (DSRTypes::getAndCheckElementFromDataset(dataset, RationalNumeratorValue, "1", "1C", "MeasuredValueSequence").good()) DSRTypes::getAndCheckElementFromDataset(dataset, RationalDenominatorValue, "1", "1" /* was 1C */, "MeasuredValueSequence"); /* read MeasurementUnitsCodeSequence */ result = MeasurementUnit.readSequence(dataset, DCM_MeasurementUnitsCodeSequence, "1" /*type*/, flags); } return result; } OFCondition DSRNumericMeasurementValue::writeItem(DcmItem &dataset) const { /* write NumericValue */ OFCondition result = DSRTypes::putStringValueToDataset(dataset, DCM_NumericValue, NumericValue); /* write some optional attributes */ DSRTypes::addElementToDataset(result, dataset, new DcmFloatingPointDouble(FloatingPointValue), "1", "1C", "MeasuredValueSequence"); DSRTypes::addElementToDataset(result, dataset, new DcmSignedLong(RationalNumeratorValue), "1", "1C", "MeasuredValueSequence"); DSRTypes::addElementToDataset(result, dataset, new DcmUnsignedLong(RationalDenominatorValue), "1", "1C", "MeasuredValueSequence"); /* write MeasurementUnitsCodeSequence */ if (result.good()) result = MeasurementUnit.writeSequence(dataset, DCM_MeasurementUnitsCodeSequence); return result; } OFCondition DSRNumericMeasurementValue::readSequence(DcmItem &dataset, const size_t flags) { /* read MeasuredValueSequence */ DcmSequenceOfItems *dseq = NULL; OFCondition result = dataset.findAndGetSequence(DCM_MeasuredValueSequence, dseq); DSRTypes::checkElementValue(dseq, DCM_MeasuredValueSequence, "1", "2", result, "NUM content item"); if (result.good()) { /* check for empty sequence (allowed!) */ if (!dseq->isEmpty()) { /* read first item */ DcmItem *ditem = dseq->getItem(0); if (ditem != NULL) result = readItem(*ditem, flags); else result = SR_EC_InvalidDocumentTree; } } if (result.good()) { /* read NumericValueQualifierCodeSequence (optional) */ ValueQualifier.readSequence(dataset, DCM_NumericValueQualifierCodeSequence, "3" /*type*/, flags); } return result; } OFCondition DSRNumericMeasurementValue::writeSequence(DcmItem &dataset) const { OFCondition result = EC_MemoryExhausted; /* write MeasuredValueSequence */ DcmSequenceOfItems *dseq = new DcmSequenceOfItems(DCM_MeasuredValueSequence); if (dseq != NULL) { /* check for empty value */ if (isEmpty()) result = EC_Normal; else { DcmItem *ditem = new DcmItem(); if (ditem != NULL) { /* write item */ result = writeItem(*ditem); if (result.good()) dseq->insert(ditem); else delete ditem; } else result = EC_MemoryExhausted; } /* write sequence (might be empty) */ if (result.good()) result = dataset.insert(dseq, OFTrue /*replaceOld*/); if (result.bad()) delete dseq; } if (result.good()) { /* write NumericValueQualifierCodeSequence (optional) */ if (!ValueQualifier.isEmpty()) ValueQualifier.writeSequence(dataset, DCM_NumericValueQualifierCodeSequence); } return result; } OFCondition DSRNumericMeasurementValue::renderHTML(STD_NAMESPACE ostream &docStream, STD_NAMESPACE ostream & /*annexStream*/, size_t & /*annexNumber*/, const size_t flags) const { if (isEmpty()) { /* empty value */ docStream << "empty"; } else { OFString htmlString; const OFBool fullCode = (flags & DSRTypes::HF_renderNumericUnitCodes) && ((flags & DSRTypes::HF_renderInlineCodes) || (flags & DSRTypes::HF_renderItemsSeparately)); if (!fullCode || (flags & DSRTypes::HF_useCodeDetailsTooltip)) { if (flags & DSRTypes::HF_XHTML11Compatibility) docStream << ""; else if (flags & DSRTypes::HF_HTML32Compatibility) docStream << ""; else /* HTML 4.01 */ docStream << ""; } docStream << DSRTypes::convertToHTMLString(NumericValue, htmlString, flags) << " "; /* render full code of the measurement unit (value first?) or code value only */ MeasurementUnit.renderHTML(docStream, flags, fullCode, (flags & DSRTypes::HF_useCodeMeaningAsUnit) == 0 /*valueFirst*/); if (!fullCode || (flags & DSRTypes::HF_useCodeDetailsTooltip)) { if (flags & DSRTypes::HF_HTML32Compatibility) docStream << ""; else docStream << ""; } } if (!ValueQualifier.isEmpty()) { /* render optional numeric value qualifier */ docStream << " ["; ValueQualifier.renderHTML(docStream, flags, (flags & DSRTypes::HF_renderInlineCodes) > 0 /*fullCode*/); docStream << "]"; } return EC_Normal; } OFCondition DSRNumericMeasurementValue::getValue(DSRNumericMeasurementValue &numericMeasurement) const { numericMeasurement = *this; return EC_Normal; } OFCondition DSRNumericMeasurementValue::getMeasurementUnit(DSRCodedEntryValue &measurementUnit) const { measurementUnit = MeasurementUnit; return EC_Normal; } OFCondition DSRNumericMeasurementValue::getNumericValueQualifier(DSRCodedEntryValue &valueQualifier) const { valueQualifier = ValueQualifier; return EC_Normal; } OFCondition DSRNumericMeasurementValue::getFloatingPointRepresentation(Float64 &floatingPoint) const { OFCondition result = SR_EC_RepresentationNotAvailable; /* cast away the const specifier (yes, this is ugly) */ DcmFloatingPointDouble &floatElement = OFconst_cast(DcmFloatingPointDouble &, FloatingPointValue); if (!floatElement.isEmpty()) result = floatElement.getFloat64(floatingPoint); return result; } OFCondition DSRNumericMeasurementValue::getRationalRepresentation(Sint32 &rationalNumerator, Uint32 &rationalDenominator) const { OFCondition result = SR_EC_RepresentationNotAvailable; /* cast away the const specifier (yes, this is ugly) */ DcmSignedLong &signedElement = OFconst_cast(DcmSignedLong &, RationalNumeratorValue); DcmUnsignedLong &unsignedElement = OFconst_cast(DcmUnsignedLong &, RationalDenominatorValue); /* check whether both values are present or absent */ if (signedElement.isEmpty() != unsignedElement.isEmpty()) result = EC_CorruptedData; /* determine rational numerator and denominator (if present) */ else if (!signedElement.isEmpty() && !unsignedElement.isEmpty()) { result = signedElement.getSint32(rationalNumerator); if (result.good()) result = unsignedElement.getUint32(rationalDenominator); } return result; } OFCondition DSRNumericMeasurementValue::setValue(const DSRNumericMeasurementValue &numericMeasurement, const OFBool check) { /* first set the basic parameters */ OFCondition result = setValue(numericMeasurement.NumericValue, numericMeasurement.MeasurementUnit, numericMeasurement.ValueQualifier, check); /* then the additional representations */ if (result.good()) { Float64 floatValue; if (numericMeasurement.getFloatingPointRepresentation(floatValue).good()) result = setFloatingPointRepresentation(floatValue, check); if (result.good()) { Sint32 numeratorValue; Uint32 denominatorValue; if (numericMeasurement.getRationalRepresentation(numeratorValue, denominatorValue).good()) result = setRationalRepresentation(numeratorValue, denominatorValue, check); } } return result; } OFCondition DSRNumericMeasurementValue::setValue(const OFString &numericValue, const DSRCodedEntryValue &measurementUnit, const OFBool check) { const DSRCodedEntryValue valueQualifier; /* call the function doing the real work */ return setValue(numericValue, measurementUnit, valueQualifier, check); } OFCondition DSRNumericMeasurementValue::setValue(const DSRCodedEntryValue &valueQualifier, const OFBool check) { const DSRCodedEntryValue measurementUnit; /* call the function doing the real work */ return setValue("" /*numericValue*/, measurementUnit, valueQualifier, check); } OFCondition DSRNumericMeasurementValue::setValue(const OFString &numericValue, const DSRCodedEntryValue &measurementUnit, const DSRCodedEntryValue &valueQualifier, const OFBool check) { OFCondition result = EC_Normal; if (check) { /* only check if at least one of the two values is non-empty */ if (!numericValue.empty() || !measurementUnit.isEmpty()) { /* check whether the passed values are valid */ result = checkNumericValue(numericValue); if (result.good()) result = checkMeasurementUnit(measurementUnit); } if (result.good()) result = checkNumericValueQualifier(valueQualifier); } else { /* make sure that both values are either empty or non-empty */ if (numericValue.empty() != measurementUnit.isEmpty()) result = EC_IllegalParameter; } if (result.good()) { NumericValue = numericValue; MeasurementUnit = measurementUnit; ValueQualifier = valueQualifier; /* clear additional representations */ FloatingPointValue.clear(); RationalNumeratorValue.clear(); RationalDenominatorValue.clear(); } return result; } OFCondition DSRNumericMeasurementValue::setNumericValue(const OFString &numericValue, const OFBool check) { OFCondition result = EC_Normal; if (check) { /* check whether the passed value is valid */ result = checkNumericValue(numericValue); } else { /* make sure that the mandatory values are non-empty */ if (numericValue.empty()) result = EC_IllegalParameter; } if (result.good()) { NumericValue = numericValue; /* clear additional representations */ FloatingPointValue.clear(); RationalNumeratorValue.clear(); RationalDenominatorValue.clear(); } return result; } OFCondition DSRNumericMeasurementValue::setNumericValue(const DcmElement &delem, const unsigned long pos, const OFBool check) { OFString numericValue; /* first, get the value from the element (need to cast away "const") */ OFCondition result = OFconst_cast(DcmElement &, delem).getOFString(numericValue, pos); if (result.good()) { /* then, check and set the value */ result = setNumericValue(numericValue, check); } return result; } OFCondition DSRNumericMeasurementValue::setNumericValue(DcmItem &dataset, const DcmTagKey &tagKey, const unsigned long pos, const OFBool check) { OFString numericValue; /* first, get the element value from the dataset */ OFCondition result = DSRTypes::getStringValueFromDataset(dataset, tagKey, numericValue, pos); if (result.good()) { /* then, check and set the value */ result = setNumericValue(numericValue, check); } return result; } OFCondition DSRNumericMeasurementValue::setMeasurementUnit(const DSRCodedEntryValue &measurementUnit, const OFBool check) { OFCondition result = EC_Normal; if (check) { /* check whether the passed value is valid */ result = checkMeasurementUnit(measurementUnit); } else { /* make sure that the mandatory values are non-empty */ if (measurementUnit.isEmpty()) result = EC_IllegalParameter; } if (result.good()) MeasurementUnit = measurementUnit; return result; } OFCondition DSRNumericMeasurementValue::setNumericValueQualifier(const DSRCodedEntryValue &valueQualifier, const OFBool check) { OFCondition result = EC_Normal; /* check whether the passed value is valid */ if (check) result = checkNumericValueQualifier(valueQualifier); if (result.good()) ValueQualifier = valueQualifier; return result; } OFCondition DSRNumericMeasurementValue::setFloatingPointRepresentation(const Float64 floatingPoint, const OFBool /*check*/) { /* make sure that only a single value is stored */ return FloatingPointValue.putFloat64Array(&floatingPoint, 1); } OFCondition DSRNumericMeasurementValue::setRationalRepresentation(const Sint32 rationalNumerator, const Uint32 rationalDenominator, const OFBool check) { OFCondition result = EC_Normal; /* check whether the passed values are valid */ if (check) result = checkRationalRepresentation(rationalNumerator, rationalDenominator); if (result.good()) { /* make sure that only a single value is stored */ RationalNumeratorValue.putSint32Array(&rationalNumerator, 1); RationalDenominatorValue.putUint32Array(&rationalDenominator, 1); } return result; } void DSRNumericMeasurementValue::removeFloatingPointRepresentation() { FloatingPointValue.clear(); } void DSRNumericMeasurementValue::removeRationalRepresentation() { RationalNumeratorValue.clear(); RationalDenominatorValue.clear(); } OFCondition DSRNumericMeasurementValue::checkNumericValue(const OFString &numericValue) const { /* numeric value should never be empty */ return numericValue.empty() ? SR_EC_InvalidValue : DcmDecimalString::checkStringValue(numericValue, "1"); } OFCondition DSRNumericMeasurementValue::checkMeasurementUnit(const DSRCodedEntryValue &measurementUnit) const { /* measurement unit should never be empty */ return measurementUnit.isEmpty() ? SR_EC_InvalidValue : measurementUnit.checkCurrentValue(); } OFCondition DSRNumericMeasurementValue::checkNumericValueQualifier(const DSRCodedEntryValue &valueQualifier) const { /* numeric value qualifier might be empty */ return valueQualifier.isEmpty() ? EC_Normal : valueQualifier.checkCurrentValue(); } OFCondition DSRNumericMeasurementValue::checkRationalRepresentation(const Sint32 /*rationalNumerator*/, const Uint32 rationalDenominator) const { /* avoid "division by zero" */ return (rationalDenominator == 0) ? SR_EC_InvalidValue : EC_Normal; } OFCondition DSRNumericMeasurementValue::checkCurrentValue() const { OFCondition result = checkNumericValue(NumericValue); if (result.good()) result = checkMeasurementUnit(MeasurementUnit); if (result.good()) result = checkNumericValueQualifier(ValueQualifier); if (result.good()) { Sint32 numeratorValue; Uint32 denominatorValue; result = getRationalRepresentation(numeratorValue, denominatorValue); if (result.good()) result = checkRationalRepresentation(numeratorValue, denominatorValue); else if (result == SR_EC_RepresentationNotAvailable) result = EC_Normal; } return result; } // output operators STD_NAMESPACE ostream &operator<<(STD_NAMESPACE ostream &stream, const DSRNumericMeasurementValue &numericMeasurement) { numericMeasurement.print(stream, 0 /*flags*/); return stream; }