/*******************************************************************************
* 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.explorer;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import org.dcm4che3.data.Tag;
import org.slf4j.LoggerFactory;
import org.weasis.core.api.explorer.ObservableEvent;
import org.weasis.core.api.explorer.model.DataExplorerModel;
import org.weasis.core.api.gui.util.GuiExecutor;
import org.weasis.core.api.media.MimeInspector;
import org.weasis.core.api.media.data.MediaElement;
import org.weasis.core.api.media.data.MediaSeries;
import org.weasis.core.api.media.data.MediaSeriesGroup;
import org.weasis.core.api.media.data.MediaSeriesGroupNode;
import org.weasis.core.api.media.data.Series;
import org.weasis.core.api.media.data.SeriesThumbnail;
import org.weasis.core.api.media.data.TagW;
import org.weasis.core.api.media.data.Thumbnail;
import org.weasis.core.api.util.FileUtil;
import org.weasis.core.ui.docking.UIManager;
import org.weasis.core.ui.editor.SeriesViewerFactory;
import org.weasis.core.ui.editor.ViewerPluginBuilder;
import org.weasis.core.ui.model.GraphicModel;
import org.weasis.core.ui.serialize.XmlSerializer;
import org.weasis.dicom.codec.DicomCodec;
import org.weasis.dicom.codec.DicomMediaIO;
import org.weasis.dicom.codec.DicomSpecialElement;
import org.weasis.dicom.codec.TagD;
import org.weasis.dicom.codec.TagD.Level;
public class LoadLocalDicom extends ExplorerTask<Boolean, String> {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(LoadLocalDicom.class);
private final File[] files;
private final DicomModel dicomModel;
private final boolean recursive;
private boolean openPlugin;
public LoadLocalDicom(File[] files, boolean recursive, DataExplorerModel explorerModel) {
super(Messages.getString("DicomExplorer.loading"), false); //$NON-NLS-1$
if (files == null || !(explorerModel instanceof DicomModel)) {
throw new IllegalArgumentException("invalid parameters"); //$NON-NLS-1$
}
this.dicomModel = (DicomModel) explorerModel;
this.files = files;
this.recursive = recursive;
this.openPlugin = true;
}
@Override
protected Boolean doInBackground() throws Exception {
dicomModel
.firePropertyChange(new ObservableEvent(ObservableEvent.BasicAction.LOADING_START, dicomModel, null, this));
addSelectionAndnotify(files, true);
return true;
}
@Override
protected void done() {
dicomModel
.firePropertyChange(new ObservableEvent(ObservableEvent.BasicAction.LOADING_STOP, dicomModel, null, this));
LOGGER.info("End of loading DICOM locally"); //$NON-NLS-1$
}
public void addSelectionAndnotify(File[] file, boolean firstLevel) {
if (file == null || file.length < 1) {
return;
}
final ArrayList<SeriesThumbnail> thumbs = new ArrayList<>();
final ArrayList<File> folders = new ArrayList<>();
for (int i = 0; i < file.length; i++) {
if (isCancelled()) {
return;
}
if (file[i] == null) {
continue;
} else if (file[i].isDirectory()) {
if (firstLevel || recursive) {
folders.add(file[i]);
}
} else {
if (file[i].canRead()) {
if (FileUtil.isFileExtensionMatching(file[i], DicomCodec.FILE_EXTENSIONS)
|| MimeInspector.isMatchingMimeTypeFromMagicNumber(file[i], DicomMediaIO.MIMETYPE)) {
DicomMediaIO loader = new DicomMediaIO(file[i]);
if (loader.isReadableDicom()) {
// Issue: must handle adding image to viewer and building thumbnail (middle image)
SeriesThumbnail t = buildDicomStructure(loader, openPlugin);
if (t != null) {
thumbs.add(t);
}
File gpxFile = new File(file[i].getPath() + ".xml"); //$NON-NLS-1$
GraphicModel graphicModel = XmlSerializer.readPresentationModel(gpxFile);
if (graphicModel != null) {
loader.setTag(TagW.PresentationModel, graphicModel);
}
}
}
}
}
}
for (final SeriesThumbnail t : thumbs) {
MediaSeries<MediaElement> series = t.getSeries();
// Avoid to rebuild most of CR series thumbnail
if (series != null && series.size(null) > 2) {
GuiExecutor.instance().execute(t::reBuildThumbnail);
}
}
for (int i = 0; i < folders.size(); i++) {
addSelectionAndnotify(folders.get(i).listFiles(), false);
}
}
private SeriesThumbnail buildDicomStructure(DicomMediaIO dicomReader, boolean open) {
SeriesThumbnail thumb = null;
String studyUID = (String) dicomReader.getTagValue(TagD.getUID(Level.STUDY));
String patientPseudoUID = (String) dicomReader.getTagValue(TagD.getUID(Level.PATIENT));
MediaSeriesGroup patient = dicomModel.getHierarchyNode(MediaSeriesGroupNode.rootNode, patientPseudoUID);
if (patient == null) {
MediaSeriesGroup study = dicomModel.getStudyNode(studyUID);
if (study == null) {
patient =
new MediaSeriesGroupNode(TagW.PatientPseudoUID, patientPseudoUID, DicomModel.patient.getTagView());
dicomReader.writeMetaData(patient);
dicomModel.addHierarchyNode(MediaSeriesGroupNode.rootNode, patient);
LOGGER.info("Adding patient: {}", patient); //$NON-NLS-1$
} else {
patient = dicomModel.getParent(study, DicomModel.patient);
LOGGER.warn("DICOM patient attributes are inconsitent! Name or ID is different within an exam."); //$NON-NLS-1$
}
}
MediaSeriesGroup study = dicomModel.getHierarchyNode(patient, studyUID);
if (study == null) {
study = new MediaSeriesGroupNode(TagD.getUID(Level.STUDY), studyUID, DicomModel.study.getTagView());
dicomReader.writeMetaData(study);
dicomModel.addHierarchyNode(patient, study);
}
String seriesUID = (String) dicomReader.getTagValue(TagD.get(Tag.SeriesInstanceUID));
Series dicomSeries = (Series) dicomModel.getHierarchyNode(study, seriesUID);
try {
if (dicomSeries == null) {
dicomSeries = dicomReader.buildSeries(seriesUID);
dicomSeries.setTag(TagW.ExplorerModel, dicomModel);
dicomReader.writeMetaData(dicomSeries);
dicomModel.addHierarchyNode(study, dicomSeries);
MediaElement[] medias = dicomReader.getMediaElement();
if (medias != null) {
for (MediaElement media : medias) {
dicomModel.applySplittingRules(dicomSeries, media);
}
if (medias.length > 0) {
dicomSeries.setFileSize(dicomSeries.getFileSize() + medias[0].getLength());
}
}
// Load image and create thumbnail in this Thread
SeriesThumbnail t = (SeriesThumbnail) dicomSeries.getTagValue(TagW.Thumbnail);
if (t == null) {
t = DicomExplorer.createThumbnail(dicomSeries, dicomModel, Thumbnail.DEFAULT_SIZE);
dicomSeries.setTag(TagW.Thumbnail, t);
t.repaint();
}
if (DicomModel.isSpecialModality(dicomSeries)) {
dicomModel.addSpecialModality(dicomSeries);
Arrays.stream(medias).filter(DicomSpecialElement.class::isInstance)
.map(DicomSpecialElement.class::cast).findFirst().ifPresent(d -> dicomModel.firePropertyChange(
new ObservableEvent(ObservableEvent.BasicAction.UPDATE, dicomModel, null, d)));
} else {
dicomModel.firePropertyChange(
new ObservableEvent(ObservableEvent.BasicAction.ADD, dicomModel, null, dicomSeries));
}
// After the thumbnail is sent to interface, it will be return to be rebuilt later
thumb = t;
Integer splitNb = (Integer) dicomSeries.getTagValue(TagW.SplitSeriesNumber);
if (splitNb != null) {
dicomModel.firePropertyChange(
new ObservableEvent(ObservableEvent.BasicAction.UPDATE, dicomModel, null, dicomSeries));
}
if (open) {
SeriesViewerFactory plugin = UIManager.getViewerFactory(dicomSeries.getMimeType());
if (plugin != null && !(plugin instanceof MimeSystemAppFactory)) {
openPlugin = false;
ViewerPluginBuilder.openSequenceInPlugin(plugin, dicomSeries, dicomModel, true, true);
} else if (plugin != null) {
// Send event to select the related patient in Dicom Explorer.
dicomModel.firePropertyChange(
new ObservableEvent(ObservableEvent.BasicAction.SELECT, dicomModel, null, dicomSeries));
}
}
} else {
// Test if SOPInstanceUID already exists
if (isSOPInstanceUIDExist(study, dicomSeries, seriesUID,
TagD.getTagValue(dicomReader, Tag.SOPInstanceUID, String.class))) {
return null;
}
MediaElement[] medias = dicomReader.getMediaElement();
if (medias != null) {
for (MediaElement media : medias) {
dicomModel.applySplittingRules(dicomSeries, media);
}
if (medias.length > 0) {
dicomSeries.setFileSize(dicomSeries.getFileSize() + medias[0].getLength());
// Refresh the number of images on the thumbnail
Thumbnail t = (Thumbnail) dicomSeries.getTagValue(TagW.Thumbnail);
if (t != null) {
t.repaint();
}
}
if (DicomModel.isSpecialModality(dicomSeries)) {
dicomModel.addSpecialModality(dicomSeries);
Arrays.stream(medias).filter(DicomSpecialElement.class::isInstance)
.map(DicomSpecialElement.class::cast).findFirst().ifPresent(d -> dicomModel.firePropertyChange(
new ObservableEvent(ObservableEvent.BasicAction.UPDATE, dicomModel, null, d)));
}
// If Split series or special DICOM element update the explorer view and View2DContainer
Integer splitNb = (Integer) dicomSeries.getTagValue(TagW.SplitSeriesNumber);
if (splitNb != null) {
dicomModel.firePropertyChange(
new ObservableEvent(ObservableEvent.BasicAction.UPDATE, dicomModel, null, dicomSeries));
}
}
}
} catch (Exception e) {
LOGGER.error("Build DicomModel", e); //$NON-NLS-1$
} finally {
// dicomReader.reset();
}
return thumb;
}
private boolean isSOPInstanceUIDExist(MediaSeriesGroup study, Series dicomSeries, String seriesUID, Object sopUID) {
TagW sopTag = TagD.getUID(Level.INSTANCE);
if (dicomSeries.hasMediaContains(sopTag, sopUID)) {
return true;
}
Object splitNb = dicomSeries.getTagValue(TagW.SplitSeriesNumber);
if (splitNb != null && study != null) {
String uid = TagD.getTagValue(dicomSeries, Tag.SeriesInstanceUID, String.class);
if (uid != null) {
Collection<MediaSeriesGroup> seriesList = dicomModel.getChildren(study);
for (Iterator<MediaSeriesGroup> it = seriesList.iterator(); it.hasNext();) {
MediaSeriesGroup group = it.next();
if (dicomSeries != group && group instanceof Series) {
Series s = (Series) group;
if (uid.equals(TagD.getTagValue(group, Tag.SeriesInstanceUID))) {
if (s.hasMediaContains(sopTag, sopUID)) {
return true;
}
}
}
}
}
}
return false;
}
}