/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.dicom.codec;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Sequence;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.UID;
import org.dcm4che3.io.DicomOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.weasis.core.api.media.data.MediaElement;
import org.weasis.core.api.media.data.SeriesComparator;
import org.weasis.core.api.util.StringUtil;
import org.weasis.core.api.util.StringUtil.Suffix;
import org.weasis.dicom.codec.macro.SOPInstanceReferenceAndMAC;
import org.weasis.dicom.codec.utils.DicomMediaUtils;
public class DicomSpecialElement extends MediaElement {
private static final Logger LOGGER = LoggerFactory.getLogger(DicomSpecialElement.class);
public static final SeriesComparator<DicomSpecialElement> ORDER_BY_DESCRIPTION =
new SeriesComparator<DicomSpecialElement>() {
@Override
public int compare(DicomSpecialElement arg0, DicomSpecialElement arg1) {
return String.CASE_INSENSITIVE_ORDER.compare(arg0.getLabel(), arg1.getLabel());
}
};
public static final SeriesComparator<DicomSpecialElement> ORDER_BY_DATE =
new SeriesComparator<DicomSpecialElement>() {
@Override
public int compare(DicomSpecialElement m1, DicomSpecialElement m2) {
// Note : Dicom Standard PS3.3 - Table C.17.6-1 KEY OBJECT DOCUMENT SERIES MODULE ATTRIBUTES
//
// SeriesDate stands for "Date the Series started" and is optional parameter, don't use this to compare
// and prefer "Content Date And Time" Tags (date and time the document content creation started)
LocalDateTime date1 = TagD.dateTime(Tag.ContentDate, Tag.ContentTime, m1);
LocalDateTime date2 = TagD.dateTime(Tag.ContentDate, Tag.ContentTime, m2);
if (date1 == null || date2 == null) {
// SeriesDate and time
date1 = TagD.dateTime(Tag.SeriesDate, Tag.SeriesTime, m1);
date2 = TagD.dateTime(Tag.SeriesDate, Tag.SeriesTime, m2);
}
if (date1 != null && date2 != null) {
// inverse time
int comp = date2.compareTo(date1);
if (comp != 0) {
return comp;
}
}
// Note : SeriesNumber stands for a number that identifies the Series.
// No specific semantics are specified.
Integer val1 = TagD.getTagValue(m1, Tag.SeriesNumber, Integer.class);
Integer val2 = TagD.getTagValue(m2, Tag.SeriesNumber, Integer.class);
if (val1 != null && val2 != null) {
// inverse number
int comp = val1 > val2 ? -1 : (val1 == val2 ? 0 : 1);
if (comp != 0) {
return comp;
}
}
return String.CASE_INSENSITIVE_ORDER.compare(m1.getLabel(), m2.getLabel());
}
};
protected String label;
public DicomSpecialElement(DicomMediaIO mediaIO) {
super(mediaIO, null);
initLabel();
}
protected String getLabelPrefix() {
StringBuilder buf = new StringBuilder();
String modality = TagD.getTagValue(this, Tag.Modality, String.class);
if (modality != null) {
buf.append(modality);
buf.append(" "); //$NON-NLS-1$
}
Integer val = TagD.getTagValue(this, Tag.InstanceNumber, Integer.class);
if (val != null) {
buf.append("["); //$NON-NLS-1$
buf.append(val);
buf.append("] "); //$NON-NLS-1$
}
return buf.toString();
}
protected void initLabel() {
StringBuilder buf = new StringBuilder(getLabelPrefix());
String desc = TagD.getTagValue(this, Tag.SeriesDescription, String.class);
if (desc != null) {
buf.append(desc);
}
label = buf.toString();
}
@Override
public DicomMediaIO getMediaReader() {
return (DicomMediaIO) super.getMediaReader();
}
public String getShortLabel() {
return StringUtil.getTruncatedString(label, 50, Suffix.THREE_PTS);
}
public String getLabel() {
return label;
}
@Override
public String toString() {
String modality = TagD.getTagValue(this, Tag.Modality, String.class);
int prefix = modality == null ? 0 : modality.length() + 1;
String l = getShortLabel();
return l.length() > prefix ? label.substring(prefix) : l;
}
@Override
public boolean saveToFile(File output) {
// When object is in memory, write it
if (getMediaReader().isEditableDicom()) {
Attributes dcm = getMediaReader().getDicomObject();
if (dcm != null) {
try (DicomOutputStream out = new DicomOutputStream(output)) {
out.writeDataset(dcm.createFileMetaInformation(UID.ImplicitVRLittleEndian), dcm);
return true;
} catch (IOException e) {
LOGGER.error("Cannot write dicom ({}): {}", getLabel(), e); //$NON-NLS-1$
}
}
}
return super.saveToFile(output);
}
public static final List<DicomSpecialElement> getPRfromSopUID(String seriesUID, String sopUID, Integer frameNumber,
List<DicomSpecialElement> studyElements) {
List<DicomSpecialElement> filteredList = new ArrayList<>();
if (studyElements != null && seriesUID != null && sopUID != null) {
for (DicomSpecialElement dicom : studyElements) {
if (dicom != null && "PR".equals(TagD.getTagValue(dicom, Tag.Modality))) { //$NON-NLS-1$
Attributes[] seq = TagD.getTagValue(dicom, Tag.ReferencedSeriesSequence, Attributes[].class);
if (isSopuidInReferencedSeriesSequence(seq, seriesUID, sopUID, frameNumber)) {
filteredList.add(dicom);
}
}
}
}
return filteredList;
}
public static boolean isSopuidInReferencedSeriesSequence(Attributes[] seq, String seriesUID, String sopUID,
Integer dicomFrameNumber) {
if (seq != null) {
for (Attributes item : seq) {
if (seriesUID.equals(item.getString(Tag.SeriesInstanceUID))) {
Sequence refImgs = item.getSequence(Tag.ReferencedImageSequence);
if (refImgs == null || refImgs.isEmpty()) {
return true;
}
for (Attributes sop : refImgs) {
if (sopUID.equals(sop.getString(Tag.ReferencedSOPInstanceUID))) {
if (dicomFrameNumber == null) {
return true;
}
int[] seqFrame =
DicomMediaUtils.getIntAyrrayFromDicomElement(sop, Tag.ReferencedFrameNumber, null);
if (seqFrame == null || seqFrame.length == 0) {
return true;
} else {
for (int k : seqFrame) {
if (k == dicomFrameNumber) {
return true;
}
}
}
}
}
}
}
}
return false;
}
public static boolean isSopuidInReferencedSeriesSequence(Map<String, SOPInstanceReferenceAndMAC> seq, String sopUID,
Integer dicomFrameNumber) {
if (seq != null && StringUtil.hasText(sopUID) && seq.containsKey(sopUID)) {
if (dicomFrameNumber != null && dicomFrameNumber > 1) {
SOPInstanceReferenceAndMAC val = seq.get(sopUID);
int[] seqFrame = val == null ? null : val.getReferencedFrameNumber();
if (seqFrame == null || seqFrame.length == 0) {
return true;
} else {
for (int k : seqFrame) {
if (k == dicomFrameNumber) {
return true;
}
}
}
} else {
return true;
}
}
return false;
}
/**
*
* @param seriesUID
* @param specialElements
* @return the KOSpecialElement collection for the given parameters, if the referenced seriesUID is null all the
* KOSpecialElement from specialElements collection are returned. In any case all the KOSpecialElement that
* are writable will be added to the returned collection whatever is the seriesUID. These KO are part of the
* new created ones by users of the application
*/
public static final Collection<KOSpecialElement> getKoSpecialElements(
Collection<DicomSpecialElement> specialElements, String seriesUID) {
if (specialElements == null) {
return Collections.emptySet();
}
SortedSet<KOSpecialElement> koElementSet = null;
for (DicomSpecialElement element : specialElements) {
if (element instanceof KOSpecialElement) {
KOSpecialElement koElement = (KOSpecialElement) element;
Set<String> referencedSeriesInstanceUIDSet = koElement.getReferencedSeriesInstanceUIDSet();
if (seriesUID == null || referencedSeriesInstanceUIDSet.contains(seriesUID)
|| koElement.getMediaReader().isEditableDicom()) {
if (koElementSet == null) {
koElementSet = new TreeSet<>(ORDER_BY_DATE);
}
koElementSet.add(koElement);
}
}
}
return koElementSet == null ? Collections.emptySet() : koElementSet;
}
public static final Collection<RejectedKOSpecialElement> getRejectionKoSpecialElements(
Collection<DicomSpecialElement> specialElements, String seriesUID) {
if (specialElements == null) {
return Collections.emptySet();
}
SortedSet<RejectedKOSpecialElement> koElementSet = null;
for (DicomSpecialElement element : specialElements) {
if (element instanceof RejectedKOSpecialElement) {
RejectedKOSpecialElement koElement = (RejectedKOSpecialElement) element;
Set<String> referencedSeriesInstanceUIDSet = koElement.getReferencedSeriesInstanceUIDSet();
if (seriesUID == null || referencedSeriesInstanceUIDSet.contains(seriesUID)
|| koElement.getMediaReader().isEditableDicom()) {
if (koElementSet == null) {
koElementSet = new TreeSet<>(ORDER_BY_DATE);
}
koElementSet.add(koElement);
}
}
}
return koElementSet == null ? Collections.emptySet() : koElementSet;
}
public static final RejectedKOSpecialElement getRejectionKoSpecialElement(
Collection<DicomSpecialElement> specialElements, String seriesUID, String sopUID, Integer dicomFrameNumber) {
if (specialElements == null) {
return null;
}
List<RejectedKOSpecialElement> koList = null;
for (DicomSpecialElement element : specialElements) {
if (element instanceof RejectedKOSpecialElement) {
RejectedKOSpecialElement koElement = (RejectedKOSpecialElement) element;
if (isSopuidInReferencedSeriesSequence(koElement.getReferencedSOPInstanceUIDObject(seriesUID), sopUID,
dicomFrameNumber)) {
if (koList == null) {
koList = new ArrayList<>();
}
koList.add(koElement);
}
}
}
if (koList != null) {
// return the most recent Rejection Object
Collections.sort(koList, ORDER_BY_DATE);
return koList.get(0);
}
return null;
}
public static final List<PRSpecialElement> getPRSpecialElements(Collection<DicomSpecialElement> specialElements,
String seriesUID, String sopUID, Integer dicomFrameNumber) {
if (specialElements == null) {
return Collections.emptyList();
}
List<PRSpecialElement> prList = null;
for (DicomSpecialElement element : specialElements) {
if (element instanceof PRSpecialElement) {
PRSpecialElement prElement = (PRSpecialElement) element;
Attributes[] seq = TagD.getTagValue(prElement, Tag.ReferencedSeriesSequence, Attributes[].class);
if (isSopuidInReferencedSeriesSequence(seq, seriesUID, sopUID, dicomFrameNumber)) {
if (prList == null) {
prList = new ArrayList<>();
}
prList.add(prElement);
}
}
}
if (prList != null) {
Collections.sort(prList, ORDER_BY_DATE);
}
return prList == null ? Collections.emptyList() : prList;
}
}