/******************************************************************************* * 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 static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; import java.io.InputStream; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAccessor; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.TimeZone; import java.util.stream.Collectors; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.dcm4che3.data.Attributes; import org.dcm4che3.data.DatePrecision; import org.dcm4che3.data.ElementDictionary; import org.dcm4che3.data.Tag; import org.dcm4che3.data.VR; import org.dcm4che3.util.DateUtils; import org.dcm4che3.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.weasis.core.api.Messages; import org.weasis.core.api.gui.util.JMVUtils; import org.weasis.core.api.media.data.TagReadable; import org.weasis.core.api.media.data.TagUtil; import org.weasis.core.api.media.data.TagW; import org.weasis.core.api.util.FileUtil; import org.weasis.core.api.util.StringUtil; import org.weasis.dicom.codec.utils.DicomMediaUtils; public class TagD extends TagW { private static final long serialVersionUID = 8923709877411396802L; private static final Logger LOGGER = LoggerFactory.getLogger(TagD.class); static final DateTimeFormatter DICOM_DATE = new DateTimeFormatterBuilder().appendValue(YEAR, 4) .appendValue(MONTH_OF_YEAR, 2).appendValue(DAY_OF_MONTH, 2).toFormatter(); static final DateTimeFormatter DICOM_TIME = new DateTimeFormatterBuilder().appendValue(HOUR_OF_DAY, 2) .optionalStart().appendValue(MINUTE_OF_HOUR, 2).optionalStart().appendValue(SECOND_OF_MINUTE, 2) .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true).toFormatter(); public enum Sex { SEX_MALE("M", org.weasis.core.api.Messages.getString("TagW.Male")), //$NON-NLS-1$ //$NON-NLS-2$ SEX_FEMALE("F", org.weasis.core.api.Messages.getString("TagW.female")), //$NON-NLS-1$ //$NON-NLS-2$ SEX_OTHER("O", org.weasis.core.api.Messages.getString("TagW.other")); //$NON-NLS-1$ //$NON-NLS-2$ private final String value; private final String displayValue; private Sex(String value, String displayValue) { this.value = value; this.displayValue = displayValue; } public String getValue() { return value; } @Override public String toString() { return displayValue; } public static Sex getSex(String sex) { Sex s = Sex.SEX_OTHER; if (StringUtil.hasText(sex)) { // Sex attribute can have the following values: M(male), F(female), or O(other) return sex.toUpperCase().startsWith("F") ? Sex.SEX_FEMALE //$NON-NLS-1$ : sex.toUpperCase().startsWith("M") ? Sex.SEX_MALE : s; //$NON-NLS-1$ } return s; } } public enum Level { PATIENT("Patient"), //$NON-NLS-1$ STUDY("Study"), //$NON-NLS-1$ SERIES("Series"), //$NON-NLS-1$ INSTANCE("Instance"), //$NON-NLS-1$ FRAME("Frame"); //$NON-NLS-1$ private final String tag; private Level(String tag) { this.tag = tag; } public String getTagName() { return tag; } @Override public String toString() { return tag; } } static { readTags(); } protected final VR vr; protected final String privateCreatorID; protected final boolean retired; public TagD(int tagID) { this(tagID, null, null, ElementDictionary.vrOf(tagID, null), 1, 1, null); } public TagD(int tagID, int vmMax) { this(tagID, null, null, ElementDictionary.vrOf(tagID, null), vmMax, vmMax, null); } public TagD(int tagID, int vmMin, int vmMax) { this(tagID, null, null, ElementDictionary.vrOf(tagID, null), vmMin, vmMax, null); } public TagD(int tagID, int vmMin, int vmMax, Object defaultValue) { this(tagID, null, null, ElementDictionary.vrOf(tagID, null), vmMin, vmMax, defaultValue); } public TagD(int tagID, String privateCreatorID) { this(tagID, null, privateCreatorID, ElementDictionary.vrOf(tagID, privateCreatorID), 1, 1, null); } public TagD(int tagID, String privateCreatorID, int vmMin, int vmMax, Object defaultValue) { this(tagID, null, privateCreatorID, ElementDictionary.vrOf(tagID, privateCreatorID), vmMin, vmMax, defaultValue); } public TagD(int tagID, String displayedName, String privateCreatorID, int vmMin, int vmMax, Object defaultValue) { this(tagID, displayedName, privateCreatorID, ElementDictionary.vrOf(tagID, privateCreatorID), vmMin, vmMax, defaultValue); } public TagD(int tagID, String keyword, String displayedName, String privateCreatorID, VR vr, int vmMin, int vmMax, Object defaultValue, boolean retired) { super(tagID, keyword, displayedName, getTypeFromTag(tagID, vr), vmMin, vmMax, defaultValue); this.vr = vr; this.privateCreatorID = privateCreatorID; this.retired = retired; } private TagD(int tagID, String displayedName, String privateCreatorID, VR vr, int vmMin, int vmMax, Object defaultValue) { super(tagID, getKeywordFromTag(tagID, privateCreatorID), displayedName, getTypeFromTag(tagID, vr), vmMin, vmMax, defaultValue); this.vr = vr; this.privateCreatorID = privateCreatorID; this.retired = false; } public VR getValueRerpesentation() { return vr; } public int getDicomValueMultiplicity(Object value) { if (value == null) { return 0; } try { return vr.vmOf(value); } catch (Exception e) { LOGGER.error("Cannot evaluate mulitplicity from DICOM VR", e); //$NON-NLS-1$ } return getValueMultiplicity(value); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((privateCreatorID == null) ? 0 : privateCreatorID.hashCode()); result = prime * result + ((vr == null) ? 0 : vr.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } TagD other = (TagD) obj; if (privateCreatorID == null) { if (other.privateCreatorID != null) { return false; } } else if (!privateCreatorID.equals(other.privateCreatorID)) { return false; } if (vr != other.vr) { return false; } return true; } @Override public Object getValue(Object data) { Object value = null; if (data instanceof Attributes) { value = readValue((Attributes) data); } else if (data instanceof XMLStreamReader) { value = readValue((XMLStreamReader) data); } else if (data instanceof String) { value = readValue((String) data); } return value; } private Object readValue(Attributes dataset) { Object value; if (isStringFamilyType()) { value = vmMax > 1 ? DicomMediaUtils.getStringArrayFromDicomElement(dataset, id, privateCreatorID, (String[]) defaultValue) : dataset.getString(privateCreatorID, id, (String) defaultValue); } else if (TagType.DICOM_DATE.equals(type) || TagType.DICOM_TIME.equals(type) || TagType.DICOM_DATETIME.equals(type)) { value = vmMax > 1 ? DicomMediaUtils.getDatesFromDicomElement(type, dataset, id, privateCreatorID, (TemporalAccessor[]) defaultValue) : DicomMediaUtils.getDateFromDicomElement(type, dataset, id, privateCreatorID, (TemporalAccessor) defaultValue); } else if (TagType.INTEGER.equals(type)) { value = vmMax > 1 ? DicomMediaUtils.getIntArrayFromDicomElement(dataset, id, privateCreatorID, (int[]) defaultValue) : DicomMediaUtils.getIntegerFromDicomElement(dataset, id, privateCreatorID, (Integer) defaultValue); } else if (TagType.FLOAT.equals(type)) { value = vmMax > 1 ? DicomMediaUtils.getFloatArrayFromDicomElement(dataset, id, privateCreatorID, (float[]) defaultValue) : DicomMediaUtils.getFloatFromDicomElement(dataset, id, privateCreatorID, (Float) defaultValue); } else if (TagType.DOUBLE.equals(type)) { value = vmMax > 1 ? DicomMediaUtils.getDoubleArrayFromDicomElement(dataset, id, privateCreatorID, (double[]) defaultValue) : DicomMediaUtils.getDoubleFromDicomElement(dataset, id, privateCreatorID, (Double) defaultValue); } else if (TagType.DICOM_SEQUENCE.equals(type)) { value = dataset.getSequence(privateCreatorID, id); } else { value = dataset.getSafeBytes(privateCreatorID, id); } return value; } private Object readValue(XMLStreamReader xmler) { Object value; if (isStringFamilyType()) { value = vmMax > 1 ? TagUtil.getStringArrayTagAttribute(xmler, keyword, (String[]) defaultValue) : TagUtil.getTagAttribute(xmler, keyword, (String) defaultValue); } else if (TagType.DICOM_DATE.equals(type) || TagType.DICOM_TIME.equals(type) || TagType.DICOM_DATETIME.equals(type)) { value = vmMax > 1 ? getDatesFromElement(xmler, keyword, type, (TemporalAccessor[]) defaultValue) : getDateFromElement(xmler, keyword, type, (TemporalAccessor) defaultValue); } else if (TagType.INTEGER.equals(type)) { value = vmMax > 1 ? TagUtil.getIntArrayTagAttribute(xmler, keyword, (int[]) defaultValue) : TagUtil.getIntegerTagAttribute(xmler, keyword, (Integer) defaultValue); } else if (TagType.FLOAT.equals(type)) { value = vmMax > 1 ? TagUtil.getFloatArrayTagAttribute(xmler, keyword, (float[]) defaultValue) : TagUtil.getFloatTagAttribute(xmler, keyword, (Float) defaultValue); } else if (TagType.DOUBLE.equals(type)) { value = vmMax > 1 ? TagUtil.getDoubleArrayTagAttribute(xmler, keyword, (double[]) defaultValue) : TagUtil.getDoubleTagAttribute(xmler, keyword, (Double) defaultValue); } else if (TagType.DICOM_SEQUENCE.equals(type)) { value = TagUtil.getTagAttribute(xmler, keyword, (String) defaultValue); } else { value = vmMax > 1 ? TagUtil.getStringArrayTagAttribute(xmler, keyword, (String[]) defaultValue) : TagUtil.getTagAttribute(xmler, keyword, (String) defaultValue); } return value; } private Object readValue(String data) { if (!StringUtil.hasText(data)) { return null; } Object value; if (isStringFamilyType()) { if (vmMax > 1) { value = toStrings(data); } else { value = data; } } else if (TagType.DICOM_DATE.equals(type)) { if (vmMax > 1) { String[] ss = toStrings(data); LocalDate[] is = new LocalDate[ss.length]; for (int i = 0; i < is.length; i++) { is[i] = TagD.getDicomDate(data); } value = is; } else { value = TagD.getDicomDate(data); } } else if (TagType.DICOM_TIME.equals(type)) { if (vmMax > 1) { String[] ss = toStrings(data); LocalTime[] is = new LocalTime[ss.length]; for (int i = 0; i < is.length; i++) { is[i] = TagD.getDicomTime(data); } value = is; } else { value = TagD.getDicomTime(data); } } else if (TagType.DICOM_DATETIME.equals(type)) { if (vmMax > 1) { String[] ss = toStrings(data); LocalDateTime[] is = new LocalDateTime[ss.length]; for (int i = 0; i < is.length; i++) { is[i] = TagD.getDicomDateTime(null, data); } value = is; } else { value = TagD.getDicomDateTime(null, data); } } else if (TagType.INTEGER.equals(type)) { if (vmMax > 1) { String[] ss = toStrings(data); int[] ds = new int[ss.length]; for (int i = 0; i < ds.length; i++) { String s = ss[i]; ds[i] = (s != null && !s.isEmpty()) ? StringUtils.parseIS(s) : 0; } value = ds; } else { value = StringUtils.parseIS(data); } } else if (TagType.FLOAT.equals(type)) { if (vmMax > 1) { String[] ss = toStrings(data); float[] ds = new float[ss.length]; for (int i = 0; i < ds.length; i++) { String s = ss[i]; ds[i] = (s != null && !s.isEmpty()) ? (float) StringUtils.parseDS(s) : Float.NaN; } value = ds; } else { value = (float) StringUtils.parseDS(data); } } else if (TagType.DOUBLE.equals(type)) { if (vmMax > 1) { String[] ss = toStrings(data); double[] ds = new double[ss.length]; for (int i = 0; i < ds.length; i++) { String s = ss[i]; ds[i] = (s != null && !s.isEmpty()) ? StringUtils.parseDS(s) : Double.NaN; } value = ds; } else { value = StringUtils.parseDS(data); } } else if (TagType.DICOM_SEQUENCE.equals(type)) { value = data; } else { value = data.getBytes(); } return value; } private static String[] toStrings(String val) { return StringUtils.split(val, '\\'); } @Override public boolean isStringFamilyType() { return TagType.STRING.equals(type) || TagType.TEXT.equals(type) || TagType.URI.equals(type) || TagType.DICOM_PERSON_NAME.equals(type) || TagType.DICOM_PERIOD.equals(type) || TagType.DICOM_SEX.equals(type); } @Override public String getFormattedTagValue(Object value, String format) { if (value == null) { return StringUtil.EMPTY_STRING; } if (TagType.DICOM_PERSON_NAME.equals(type)) { if (value instanceof String[]) { return Arrays.asList((String[]) value).stream().map(TagD::getDicomPersonName) .collect(Collectors.joining(", ")); //$NON-NLS-1$ } return getDicomPersonName(value.toString()); } else if (TagType.DICOM_PERIOD.equals(type)) { return getDicomPeriod(value.toString()); } else if (TagType.DICOM_SEX.equals(type)) { return getDicomPatientSex(value.toString()); } return super.getFormattedTagValue(value, format); } public static TagW get(String keyword) { // Overrides static method in TagW only to force the method readTags() if not initialized return tags.get(keyword); } public static String getKeywordFromTag(int tagID, String privateCreatorID) { return ElementDictionary.getElementDictionary(privateCreatorID).keywordOf(tagID); } private static TagType getTypeFromTag(int tagID, VR vr) { if (vr != null) { if (vr.isIntType()) { return TagType.INTEGER; } else if (vr.isTemporalType()) { if (VR.DA.equals(vr)) { return TagType.DICOM_DATE; } else if (VR.TM.equals(vr)) { return TagType.DICOM_TIME; } return TagType.DICOM_DATETIME; } else if (vr.isStringType()) { if (VR.DS.equals(vr)) { return TagType.DOUBLE; } else if (VR.PN.equals(vr)) { return TagType.DICOM_PERSON_NAME; } else if (VR.UR.equals(vr)) { return TagType.URI; } else if (VR.AS.equals(vr)) { return TagType.DICOM_PERIOD; } else if (Tag.PatientSex == tagID) { return TagType.DICOM_SEX; } else if (VR.LT.equals(vr) || VR.ST.equals(vr) || VR.UT.equals(vr)) { return TagType.TEXT; } return TagType.STRING; } else if (VR.SQ.equals(vr)) { return TagType.DICOM_SEQUENCE; } else { // Binary value type if (VR.FD.equals(vr)) { return TagType.DOUBLE; } else if (VR.FL.equals(vr)) { return TagType.FLOAT; } else if (VR.SL.equals(vr) || VR.SS.equals(vr) || VR.UL.equals(vr) || VR.US.equals(vr)) { return TagType.INTEGER; } return TagType.BYTE; } } return TagType.STRING; } private static Map<Integer, TagD> readTags() { Map<Integer, TagD> map = new HashMap<>(); XMLStreamReader xmler = null; InputStream stream = null; try { XMLInputFactory xmlif = XMLInputFactory.newInstance(); stream = TagD.class.getResourceAsStream("/dataelements.xml"); //$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 ("dataelements".equals(key)) { //$NON-NLS-1$ while (xmler.hasNext()) { eventType = xmler.next(); switch (eventType) { case XMLStreamConstants.START_ELEMENT: key = xmler.getName().getLocalPart(); if ("el".equals(key)) { //$NON-NLS-1$ readElement(xmler, map); } break; default: break; } } } break; default: break; } } } catch (Exception e) { LOGGER.error("Cannot read dataelements.xml! ", e); //$NON-NLS-1$ } finally { FileUtil.safeClose(xmler); FileUtil.safeClose(stream); } return map; } private static void readElement(XMLStreamReader xmler, Map<Integer, TagD> map) throws XMLStreamException { String tag = xmler.getAttributeValue(null, "tag");//$NON-NLS-1$ String keyword = xmler.getAttributeValue(null, "keyword");//$NON-NLS-1$ String vr = xmler.getAttributeValue(null, "vr");//$NON-NLS-1$ String vm = xmler.getAttributeValue(null, "vm");//$NON-NLS-1$ String retired = xmler.getAttributeValue(null, "retired");//$NON-NLS-1$ int eventType; boolean state = true; while (xmler.hasNext() && state) { eventType = xmler.next(); switch (eventType) { case XMLStreamConstants.CHARACTERS: if (tag != null && keyword != null && vr != null && vm != null) { String disp = xmler.getText(); if (StringUtil.hasText(disp)) { try { if (tag.startsWith("F")) { //$NON-NLS-1$ return; } int tagID = Integer.parseInt(tag.replace('x', '0'), 16); String[] vms = vm.split("-", 2); //$NON-NLS-1$ int vmMin; int vmMax; if (vms.length == 1) { vmMin = vmMax = getVM(vms[0]); } else if (vms.length == 2) { vmMin = getVM(vms[0]); vmMax = getVM(vms[1]); } else { vmMin = vmMax = 1; } String defaultValue = null; if (tagID == Tag.PatientID || tagID == Tag.PatientName || tagID == Tag.StudyInstanceUID || tagID == Tag.SeriesInstanceUID || tagID == Tag.Modality) { defaultValue = TagW.NO_VALUE; } VR vrVal = getVR(vr); TagD t; if (VR.SQ.equals(vrVal)) { t = new TagSeq(tagID, keyword, disp, null, vrVal, vmMin, vmMax, defaultValue, JMVUtils.getNULLtoFalse(retired)); } else { t = new TagD(tagID, keyword, disp, null, vrVal, vmMin, vmMax, defaultValue, JMVUtils.getNULLtoFalse(retired)); } TagW.addTag(t); } catch (Exception e) { LOGGER.error("Cannot read {}", disp, e); //$NON-NLS-1$ } } } else { // Exclude delimitation tags if (tag == null || !tag.startsWith("FFFEE0")) { //$NON-NLS-1$ LOGGER.error("Missing attribute: {} {} {} {}", //$NON-NLS-1$ new Object[] { tag, keyword, vr, vm }); // $NON-NLS-1$ } } break; case XMLStreamConstants.END_ELEMENT: if ("el".equals(xmler.getName().getLocalPart())) { //$NON-NLS-1$ state = false; } break; default: break; } } } private static int getVM(String val) { if ("n".equals(val) || val.contains("n")) { //$NON-NLS-1$ //$NON-NLS-2$ return Integer.MAX_VALUE; } return Integer.parseInt(val); } private static VR getVR(String vr) { try { return VR.valueOf(vr); } catch (Exception e) { return VR.OB; } } public static TagW get(int tagID) { return get(tagID, null); } public static TagW get(int tagID, String privateCreatorID) { TagW val = getNullable(tagID, privateCreatorID); if (val == null) { return UnknownTag; } return val; } public static TagW getNullable(int tagID) { return getNullable(tagID, null); } public static TagW getNullable(int tagID, String privateCreatorID) { String key = getKeywordFromTag(tagID, privateCreatorID); return key == null ? null : tags.get(key); } public static Object getTagValue(TagReadable tagable, int tagID) { if (tagable != null) { String key = getKeywordFromTag(tagID, null); if (key != null) { return tagable.getTagValue(tags.get(key)); } } return null; } public static <T> T getTagValue(TagReadable tagable, int tagID, Class<T> type) { if (tagable != null) { String key = getKeywordFromTag(tagID, null); if (key != null) { try { return type.cast(tagable.getTagValue(tags.get(key))); } catch (ClassCastException e) { LOGGER.error("Cannot cast the value of \"{}\" into {}", key, type, e); //$NON-NLS-1$ } } } return null; } public static TagW[] getTagFromIDs(int... tagID) { ArrayList<TagW> list = new ArrayList<>(); if (tagID != null) { for (int id : tagID) { TagW t = getNullable(id, null); if (t != null) { list.add(t); } } } return list.toArray(new TagW[list.size()]); } public static TagW getUID(Level level) { if (level != null) { switch (level) { case PATIENT: return TagW.PatientPseudoUID; case STUDY: return TagD.get(Tag.StudyInstanceUID); case SERIES: return TagW.SubseriesInstanceUID; case INSTANCE: case FRAME: return TagD.get(Tag.SOPInstanceUID); default: break; } } return TagW.UnknownTag; } public static LocalDate getDicomDate(String date) { if (Objects.nonNull(date)) { try { if (date.length() > 8) { StringBuilder buf = new StringBuilder(8); // Try to fix old format yyyy.mm.dd (prior DICOM3.0) date.chars().filter(Character::isDigit).forEachOrdered(i -> buf.append((char) i)); return LocalDate.parse(buf.toString().trim(), DICOM_DATE); } return LocalDate.parse(date, DICOM_DATE); } catch (Exception e) { LOGGER.error("Parse DICOM date", e); //$NON-NLS-1$ } } return null; } public static LocalTime getDicomTime(String time) { if (Objects.nonNull(time)) { try { return LocalTime.parse(time.trim(), DICOM_TIME); } catch (Exception e) { try { StringBuilder buf = new StringBuilder(8); // Try to fix old format HH:MM:SS.frac (prior DICOM3.0) time.chars().filter(i -> ':' != (char) i).forEachOrdered(i -> buf.append((char) i)); return LocalTime.parse(buf.toString().trim(), DICOM_TIME); } catch (Exception e1) { LOGGER.error("Parse DICOM time", e1); //$NON-NLS-1$ } } } return null; } public static LocalDateTime getDicomDateTime(TimeZone tz, String value) { return getDicomDateTime(tz, value, false); } public static LocalDateTime getDicomDateTime(TimeZone tz, String value, boolean ceil) { if (Objects.nonNull(value)) { try { Date date = DateUtils.parseDT(tz, value, ceil, new DatePrecision()); return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); } catch (Exception e) { LOGGER.error("Parse DICOM dateTime", e); //$NON-NLS-1$ } } return null; } public static LocalDateTime dateTime(long dateTimeID, Attributes attributes) { if (attributes == null) { return null; } Date date = attributes.getDate(dateTimeID); if (date == null) { return null; } return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); } public static LocalDateTime dateTime(int dateID, int timeID, TagReadable tagable) { LocalDate date = TagD.getTagValue(tagable, dateID, LocalDate.class); LocalTime time = TagD.getTagValue(tagable, timeID, LocalTime.class); if (date == null) { return null; } if (time == null) { return date.atStartOfDay(); } return LocalDateTime.of(date, time); } public static String formatDicomDate(LocalDate date) { if (date != null) { return DICOM_DATE.format(date); } return StringUtil.EMPTY_STRING; } public static String formatDicomTime(LocalTime time) { if (time != null) { return DICOM_TIME.format(time); } return StringUtil.EMPTY_STRING; } public static TemporalAccessor getDateFromElement(XMLStreamReader xmler, String attribute, TagType type, TemporalAccessor defaultValue) { if (attribute != null) { String val = xmler.getAttributeValue(null, attribute); if (val != null) { if (TagType.DICOM_TIME.equals(type)) { return getDicomTime(val); } else if (TagType.DICOM_DATETIME.equals(type)) { return getDicomDateTime(null, val); } else { return getDicomDate(val); } } } return defaultValue; } public static TemporalAccessor[] getDatesFromElement(XMLStreamReader xmler, String attribute, TagType type, TemporalAccessor[] defaultValue) { return getDatesFromElement(xmler, attribute, type, defaultValue, "\\"); //$NON-NLS-1$ } public static TemporalAccessor[] getDatesFromElement(XMLStreamReader xmler, String attribute, TagType type, TemporalAccessor[] defaultValue, String separator) { if (attribute != null) { String val = xmler.getAttributeValue(null, attribute); if (val != null) { String[] strs = val.split(separator); TemporalAccessor[] vals = new TemporalAccessor[strs.length]; for (int i = 0; i < strs.length; i++) { if (TagType.TIME.equals(type)) { vals[i] = getDicomTime(strs[i]); } else if (TagType.DATETIME.equals(type)) { vals[i] = getDicomDateTime(null, strs[i]); } else { vals[i] = getDicomDate(strs[i]); } } return vals; } } return defaultValue; } public static String getDicomPeriod(String value) { if (!StringUtil.hasText(value)) { return StringUtil.EMPTY_STRING; } // 3 digits followed by one of the characters 'D' (Day),'W' (Week), 'M' (Month) or 'Y' (Year) // For ex: DICOM (0010,1010) = 031Y if (value.length() < 2) { return ""; //$NON-NLS-1$ } String unit; switch (value.charAt(value.length() - 1)) { case 'Y': unit = ChronoUnit.YEARS.toString(); break; case 'M': unit = ChronoUnit.MONTHS.toString(); break; case 'W': unit = ChronoUnit.WEEKS.toString(); break; case 'D': unit = ChronoUnit.DAYS.toString(); break; default: LOGGER.error("Get period format: \"{}\" is not valid", value); //$NON-NLS-1$ return StringUtil.EMPTY_STRING; } // Remove the last character and leading 0 StringBuilder builder = new StringBuilder(value.substring(0, value.length() - 1).replaceFirst("^0+(?!$)", "")); //$NON-NLS-1$ //$NON-NLS-2$ builder.append(" "); //$NON-NLS-1$ builder.append(unit); return builder.toString(); } /** * @param name * @return return the name (e.g. Smith, John), representing the "lexical name order" */ public static String getDicomPersonName(String name) { if (!StringUtil.hasText(name)) { return StringUtil.EMPTY_STRING; } /* * Further internationalization issues arise in countries where the language has a phonetic or ideographic * representation, such as in Japan and Korea. For these situations, DICOM allows up to three “component * groups,” the first a single-byte representation as is used for western languages, then an ideographic (Kanji * or Hanga) representation and then a phonetic representation (Hiragana or Hangul). These are separated by ‘=’ * (0x3d) characters. */ StringBuilder buf = new StringBuilder(); String[] names = name.split("="); //$NON-NLS-1$ for (int k = 0; k < names.length; k++) { if (k > 0) { buf.append("="); //$NON-NLS-1$ } /* * In DICOM “family name^given name^middle name^prefix^suffix” * * In HL7 “family name^given name^middle name^suffix^prefix^ degree” */ String[] vals = names[k].split("\\^"); //$NON-NLS-1$ for (int i = 0; i < vals.length; i++) { if (StringUtil.hasText(vals[i])) { if (i == 1 || i >= 3) { buf.append(", "); //$NON-NLS-1$ } else { buf.append(" "); //$NON-NLS-1$ } buf.append(vals[i]); } } } return buf.toString().trim(); } public static String getDicomPatientSex(String val) { if (!StringUtil.hasText(val)) { return StringUtil.EMPTY_STRING; } return Sex.getSex(val).toString(); } }