/*******************************************************************************
* 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.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.EnumMap;
import java.util.Map;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.dcm4che3.data.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.weasis.core.api.media.data.TagW;
import org.weasis.core.api.util.FileUtil;
import org.weasis.core.api.util.ResourceUtil;
import org.weasis.core.api.util.StringUtil;
import org.weasis.dicom.codec.TagD;
import org.weasis.dicom.codec.display.Modality;
import org.weasis.dicom.codec.utils.SplittingModalityRules.And;
import org.weasis.dicom.codec.utils.SplittingModalityRules.CompositeCondition;
import org.weasis.dicom.codec.utils.SplittingModalityRules.Condition;
import org.weasis.dicom.codec.utils.SplittingModalityRules.Condition.Type;
import org.weasis.dicom.codec.utils.SplittingModalityRules.DefaultCondition;
import org.weasis.dicom.codec.utils.SplittingModalityRules.Or;
public class SplittingRules {
private static final Logger LOGGER = LoggerFactory.getLogger(SplittingRules.class);
private final Map<Modality, SplittingModalityRules> rules;
public SplittingRules() {
rules = new EnumMap<>(Modality.class);
initDefault();
readSplittingRulesFromResources();
}
private void initDefault() {
SplittingModalityRules defRules = new SplittingModalityRules(Modality.DEFAULT);
defRules.addSingleFrameTags(Tag.ImageType, null);
defRules.addSingleFrameTags(Tag.SOPClassUID, null);
defRules.addSingleFrameTags(Tag.ContrastBolusAgent, null);
defRules.addMultiFrameTags(Tag.ImageType, null);
defRules.addMultiFrameTags(Tag.SOPInstanceUID, null);
defRules.addMultiFrameTags(Tag.FrameType, null);
defRules.addMultiFrameTags(Tag.FrameAcquisitionNumber, null);
defRules.addMultiFrameTags(Tag.StackID, null);
rules.put(defRules.getModality(), defRules);
SplittingModalityRules ctRules = new SplittingModalityRules(Modality.CT, defRules);
ctRules.addSingleFrameTags(Tag.ConvolutionKernel, null);
ctRules.addSingleFrameTags(Tag.GantryDetectorTilt, null);
// Make a condition to exclude projection image type
And allOf = new And();
allOf.addChild(
new DefaultCondition(TagD.get(Tag.ImageType), Condition.Type.notContainsIgnoreCase, "PROJECTION")); //$NON-NLS-1$
ctRules.addSingleFrameTags(TagW.ImageOrientationPlane, allOf);
rules.put(ctRules.getModality(), ctRules);
SplittingModalityRules ptRules = new SplittingModalityRules(Modality.PT, defRules);
ptRules.addSingleFrameTags(Tag.ConvolutionKernel, null);
ptRules.addSingleFrameTags(Tag.GantryDetectorTilt, null);
rules.put(ptRules.getModality(), ptRules);
SplittingModalityRules mrRules = new SplittingModalityRules(Modality.MR, defRules);
mrRules.addSingleFrameTags(Tag.ScanningSequence, null);
mrRules.addSingleFrameTags(Tag.SequenceVariant, null);
mrRules.addSingleFrameTags(Tag.ScanOptions, null);
mrRules.addSingleFrameTags(Tag.RepetitionTime, null);
mrRules.addSingleFrameTags(Tag.EchoTime, null);
mrRules.addSingleFrameTags(Tag.InversionTime, null);
mrRules.addSingleFrameTags(Tag.FlipAngle, null);
// Reuse the condition for ImageOrientationPlane
mrRules.addSingleFrameTags(TagW.ImageOrientationPlane, allOf);
rules.put(mrRules.getModality(), mrRules);
}
private void readSplittingRulesFromResources() {
XMLStreamReader xmler = null;
InputStream stream = null;
try {
File file = ResourceUtil.getResource("series-splitting-rules.xml"); //$NON-NLS-1$
if (!file.canRead()) {
return;
}
XMLInputFactory xmlif = XMLInputFactory.newInstance();
stream = new FileInputStream(file); // $NON-NLS-1$
xmler = xmlif.createXMLStreamReader(stream);
int eventType;
while (xmler.hasNext()) {
eventType = xmler.next();
switch (eventType) {
case XMLStreamConstants.START_ELEMENT:
String key = xmler.getName().getLocalPart();
if ("modalities".equals(key)) { //$NON-NLS-1$
readModalities(xmler);
}
break;
default:
break;
}
}
}
catch (Exception e) {
LOGGER.error("Cannot read series-splitting-rules.xml! ", e); //$NON-NLS-1$
} finally {
FileUtil.safeClose(xmler);
FileUtil.safeClose(stream);
}
}
private void readModalities(XMLStreamReader xmler) throws XMLStreamException {
while (xmler.hasNext()) {
int eventType = xmler.next();
switch (eventType) {
case XMLStreamConstants.START_ELEMENT:
String key = xmler.getName().getLocalPart();
if ("modality".equals(key) && xmler.getAttributeCount() >= 1) { //$NON-NLS-1$
String name = xmler.getAttributeValue(null, "name");//$NON-NLS-1$
Modality m = getModdality(name);
if (m != null) {
try {
String extend = xmler.getAttributeValue(null, "extend");//$NON-NLS-1$
SplittingModalityRules splitRules =
new SplittingModalityRules(m, getSplittingModalityRules(extend));
readModality(splitRules, xmler);
rules.put(m, splitRules);
} catch (Exception e) {
LOGGER.error("Modality {} cannot be read from series-splitting-rules.xml", //$NON-NLS-1$
name, e);
}
}
}
break;
default:
break;
}
}
}
private static void readModality(SplittingModalityRules data, XMLStreamReader xmler) throws XMLStreamException {
int eventType;
boolean state = true;
while (xmler.hasNext() && state) {
eventType = xmler.next();
switch (eventType) {
case XMLStreamConstants.START_ELEMENT:
String element = xmler.getName().getLocalPart();
if ("splittingTags".equals(element)) { //$NON-NLS-1$
readTags(data, xmler, false, element);
} else if ("multiframeSplittingTags".equals(element)) { //$NON-NLS-1$
readTags(data, xmler, true, element);
}
break;
case XMLStreamConstants.END_ELEMENT:
if ("modality".equals(xmler.getName().getLocalPart())) { //$NON-NLS-1$
state = false;
}
break;
default:
break;
}
}
}
private static void readTags(SplittingModalityRules data, XMLStreamReader xmler, boolean multiframe,
String endElement) throws XMLStreamException {
int eventType;
boolean state = true;
while (xmler.hasNext() && state) {
eventType = xmler.next();
switch (eventType) {
case XMLStreamConstants.START_ELEMENT:
TagW tag = getTag(xmler.getName().getLocalPart());
if (tag != null) {
Condition condition = readCondition(data, xmler, tag.getKeyword());
if (multiframe) {
data.addMultiFrameTags(tag, condition);
} else {
data.addSingleFrameTags(tag, condition);
}
}
break;
case XMLStreamConstants.END_ELEMENT:
if (endElement.equals(xmler.getName().getLocalPart())) {
state = false;
}
break;
default:
break;
}
}
}
private static Condition readCondition(SplittingModalityRules data, XMLStreamReader xmler, String endElement)
throws XMLStreamException {
CompositeCondition conditions = null;
CompositeCondition lastConditions = null;
TagW tag = null;
Condition.Type type = null;
String value = null;
int eventType;
boolean state = true;
while (xmler.hasNext() && state) {
eventType = xmler.next();
switch (eventType) {
case XMLStreamConstants.CHARACTERS:
value = xmler.getText();
break;
case XMLStreamConstants.START_ELEMENT:
if ("condition".equals(xmler.getName().getLocalPart())) { //$NON-NLS-1$
tag = getTag(xmler.getAttributeValue(null, "tag"));//$NON-NLS-1$
type = getConditionType(xmler.getAttributeValue(null, "type"));//$NON-NLS-1$
} else if ("conditions".equals(xmler.getName().getLocalPart())) { //$NON-NLS-1$
String t = xmler.getAttributeValue(null, "type");//$NON-NLS-1$
lastConditions = "anyOf".equals(t) ? new Or() : new And(); //$NON-NLS-1$
if (conditions == null) {
// Root condition
conditions = lastConditions;
} else {
conditions.addChild(lastConditions);
}
}
break;
case XMLStreamConstants.END_ELEMENT:
if ("condition".equals(xmler.getName().getLocalPart())) { //$NON-NLS-1$
if (tag == null || type == null || value == null) {
LOGGER.error("Cannot read condition: {} {} {}", tag, type, value); //$NON-NLS-1$
}
if (lastConditions != null) {
lastConditions.addChild(new DefaultCondition(tag, type, value));
}
tag = null;
type = null;
value = null;
} else if (endElement.equals(xmler.getName().getLocalPart())) { // $NON-NLS-1$
state = false;
}
break;
default:
break;
}
}
return conditions;
}
private SplittingModalityRules getSplittingModalityRules(String extend) {
if (StringUtil.hasText(extend)) {
SplittingModalityRules val = rules.get(getModdality(extend));
if (val == null) {
LOGGER.error("Modality {} doesn't exist! Cannot ihnerit the rules.", //$NON-NLS-1$
extend);
}
}
return null;
}
public SplittingModalityRules getSplittingModalityRules(Modality key, Modality defaultKey) {
SplittingModalityRules val = rules.get(key);
if (val == null) {
val = rules.get(defaultKey);
}
return val;
}
private static Type getConditionType(String type) {
try {
return Condition.Type.valueOf(type);
} catch (Exception e) {
LOGGER.error("{} is not a valid condition type", type, e); //$NON-NLS-1$
}
return null;
}
private static Modality getModdality(String name) {
try {
return Modality.valueOf(name);
} catch (Exception e) {
LOGGER.error("Modality reference of {} is missing", name, e); //$NON-NLS-1$
}
return null;
}
private static TagW getTag(String tagKey) {
TagW tag = TagW.get(tagKey);
if (tag == null) {
LOGGER.error("Cannot find a tag with the keyword {}", tagKey); //$NON-NLS-1$
}
return tag;
}
}