From 858f364b68e1f09a5fbf0792f98def060110eb50 Mon Sep 17 00:00:00 2001 From: Nirostar <36939232+Nirostar@users.noreply.github.com> Date: Wed, 28 Sep 2022 14:46:09 +0200 Subject: [PATCH 1/5] Set version number to next SNAPSHOT --- README.MD | 6 +++--- gradle.properties | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index e76aa51..42eccc0 100644 --- a/README.MD +++ b/README.MD @@ -19,7 +19,7 @@ repositories { ```groovy dependencies { - implementation 'org.dcm4che:dcm4che-typeddicom:0.4.2' + implementation 'org.dcm4che:dcm4che-typeddicom:0.4.3-SNAPSHOT' } ``` @@ -35,7 +35,7 @@ repositories { ```kotlin dependencies { - implementation("org.dcm4che:dcm4che-typeddicom:0.4.2") + implementation("org.dcm4che:dcm4che-typeddicom:0.4.3-SNAPSHOT") } ``` @@ -54,7 +54,7 @@ dependencies { org.dcm4che dcm4che-typeddicom - 0.4.2 + 0.4.3-SNAPSHOT ``` diff --git a/gradle.properties b/gradle.properties index 7805828..70f4047 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -version=0.4.2 +version=0.4.3-SNAPSHOT org.gradle.jvmargs=-Xmx4096m org.gradle.caching=true From 82affb13e90d66ecc028892ebaf55e3b8b8ca6b9 Mon Sep 17 00:00:00 2001 From: Nirostar <36939232+Nirostar@users.noreply.github.com> Date: Thu, 29 Sep 2022 08:55:54 +0200 Subject: [PATCH 2/5] Add Builder converter method --- .../src/main/java/org/dcm4che/typeddicom/Builder.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/Builder.java b/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/Builder.java index ff930ca..307d3ba 100644 --- a/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/Builder.java +++ b/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/Builder.java @@ -19,4 +19,8 @@ default SELF setSequence(String privateCreator, int tag, Builder... itemBu } return (SELF) this; } + + default B as(Class builderClass) { + return AttributesWrapper.wrap(this.getAttributes(), builderClass); + } } From f84bfb6a39618447978d83f61fd066175555cbad Mon Sep 17 00:00:00 2001 From: Nirostar <36939232+Nirostar@users.noreply.github.com> Date: Wed, 30 Nov 2022 18:18:47 +0100 Subject: [PATCH 3/5] Add Private Creator to Sequences --- .../typeddicom/DataElementWrapper.java | 14 +++++------ .../dcm4che/typeddicom/SequenceWrapper.java | 6 +++++ .../templates/DataElement.java.mustache | 24 +++++++++++++------ 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/DataElementWrapper.java b/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/DataElementWrapper.java index fb90166..4cb7844 100644 --- a/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/DataElementWrapper.java +++ b/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/DataElementWrapper.java @@ -13,20 +13,18 @@ public interface DataElementWrapper extends AttributesWrapper { */ VR getValueRepresentation(); + /** + * @return The DICOM + * Private Creator of the wrapped DICOM attributes. null if not private + */ + String getPrivateCreator(); + /** * @return The DICOM * Tag of the wrapped DICOM attributes. */ int getTag(); - /** - * @return The DICOM - * Private Creator of the wrapped DICOM attributes. - */ - default String getPrivateCreator() { - return null; - } - Object getValue(); boolean exists(); diff --git a/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/SequenceWrapper.java b/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/SequenceWrapper.java index f9810d7..6e2b6cc 100644 --- a/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/SequenceWrapper.java +++ b/dcm4che-typeddicom-lib/src/main/java/org/dcm4che/typeddicom/SequenceWrapper.java @@ -34,6 +34,12 @@ public VR getValueRepresentation() { return VALUE_REPRESENTATION; } + /** + * @return The DICOM + * Private Creator of the wrapped DICOM attributes. null if not private + */ + public abstract String getPrivateCreator(); + /** * @return The DICOM * Tag of the wrapped DICOM sequence. diff --git a/dcm4che-typeddicom-lib/src/main/resources/templates/DataElement.java.mustache b/dcm4che-typeddicom-lib/src/main/resources/templates/DataElement.java.mustache index ff0cbf6..f716723 100644 --- a/dcm4che-typeddicom-lib/src/main/resources/templates/DataElement.java.mustache +++ b/dcm4che-typeddicom-lib/src/main/resources/templates/DataElement.java.mustache @@ -25,6 +25,11 @@ import java.util.Arrays; @Deprecated {{/retired}} public class {{keyword}} extends {{#sequence}}SequenceWrapper<{{keyword}}, {{keyword}}.Item>{{/sequence}}{{^sequence}}AbstractDataElementWrapper implements {{valueRepresentationWrapper}}{{/sequence}} { + /** + * The Private Creator + * associated with this class. + */ + public static final String PRIVATE_CREATOR = null; /** * The DICOM Tag * associated with this class. @@ -70,6 +75,11 @@ public class {{keyword}} extends {{#sequence}}SequenceWrapper<{{keyword}}, {{key } {{/sequence}} + @Override + public String getPrivateCreator() { + return PRIVATE_CREATOR; + } + @Override public int getTag() { return TAG; @@ -150,7 +160,7 @@ public class {{keyword}} extends {{#sequence}}SequenceWrapper<{{keyword}}, {{key /** * @return a {@link Builder} to create {{name}} Items with a fluent API. */ - public static Builder builder() { + static Builder builder() { return new Builder(); } @@ -159,7 +169,7 @@ public class {{keyword}} extends {{#sequence}}SequenceWrapper<{{keyword}}, {{key * * @return a {@link Builder} to create {{name}} Items with a fluent API. Instead of creating new Attributes it wraps the provided Attributes. */ - public static Builder builder(Attributes attributes) { + static Builder builder(Attributes attributes) { return new Builder(attributes); } @@ -168,7 +178,7 @@ public class {{keyword}} extends {{#sequence}}SequenceWrapper<{{keyword}}, {{key */ default {{keyword}} get{{keyword}}() { {{#sequence}} - Sequence sequence = getAttributes().getSequence(TAG); + Sequence sequence = getAttributes().getSequence(PRIVATE_CREATOR, TAG); if (sequence == null) { return null; } @@ -186,7 +196,7 @@ public class {{keyword}} extends {{#sequence}}SequenceWrapper<{{keyword}}, {{key * @param initialCapacity the initial capacity for the new {{name}} */ default {{keyword}} new{{keyword}}(int initialCapacity) { - return new {{keyword}}(getAttributes().newSequence(TAG, initialCapacity)); + return new {{keyword}}(getAttributes().newSequence(PRIVATE_CREATOR, TAG, initialCapacity)); } /** @@ -199,7 +209,7 @@ public class {{keyword}} extends {{#sequence}}SequenceWrapper<{{keyword}}, {{key * initialCapacity. */ default {{keyword}} ensure{{keyword}}(int initialCapacity) { - return new {{keyword}}(getAttributes().ensureSequence(TAG, initialCapacity)); + return new {{keyword}}(getAttributes().ensureSequence(PRIVATE_CREATOR, TAG, initialCapacity)); } /** @@ -217,14 +227,14 @@ public class {{keyword}} extends {{#sequence}}SequenceWrapper<{{keyword}}, {{key * @return Whether it contains the {{name}} (see {@link {{keyword}}}) */ default boolean contains{{keyword}}() { - return getAttributes().contains(TAG); + return getAttributes().contains(PRIVATE_CREATOR, TAG); } /** * Removes the {{name}} (see {@link {{keyword}}} */ default void remove{{keyword}}() { - getAttributes().remove(TAG); + getAttributes().remove(PRIVATE_CREATOR, TAG); } class Builder extends AbstractAttributesWrapper implements org.dcm4che.typeddicom.Builder, {{keyword}}.Builder { From 646ebc8d763d228fb97215ddace1c3d56cda28b7 Mon Sep 17 00:00:00 2001 From: Nirostar <36939232+Nirostar@users.noreply.github.com> Date: Wed, 30 Nov 2022 19:30:04 +0100 Subject: [PATCH 4/5] Add support for functional group macros --- .../typeddicom/DicomPart03Handler.java | 57 +++++++++++++------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/buildSrc/src/main/java/org/dcm4che/typeddicom/DicomPart03Handler.java b/buildSrc/src/main/java/org/dcm4che/typeddicom/DicomPart03Handler.java index 8a4174d..7887e21 100644 --- a/buildSrc/src/main/java/org/dcm4che/typeddicom/DicomPart03Handler.java +++ b/buildSrc/src/main/java/org/dcm4che/typeddicom/DicomPart03Handler.java @@ -8,12 +8,14 @@ import org.xml.sax.SAXException; import java.util.*; +import java.util.stream.Collectors; /** * This class parses the 3th part of the DICOM Standard XML * (http://dicom.nema.org/medical/dicom/current/source/docbook/part03/part03.xml) */ public class DicomPart03Handler extends MemorizeTablesDicomPartHandler { + private static final String FUNCTIONAL_GROUP_MACRO_REFERENCE = "FUNCTIONAL GROUP MACRO"; private final Set modules; private final Map> dataElementMetaInfos; private final Set iods; @@ -211,10 +213,11 @@ private void handleEndOfAttributesTableRow() { int currentSequenceDepth = sequenceDepth(name); name = name.substring(currentSequenceDepth).trim(); TableEntry currentTableEntry; - boolean validInclude = name.startsWith("Include") && + boolean validStandardInclude = name.startsWith("Include") && lastReferenceInFirstColumn != null && lastReferenceInFirstColumn.startsWith("table_"); boolean validAttribute = columns.size() > 1 && columns.get(1).matches("\\([0-9A-F]{2}[0-9A-Fx]{2},[0-9A-F]{2}[0-9A-Fx]{2}\\)"); + boolean validFunctionalGroupMacroInclude = name.startsWith("Include one or more Functional Group Macros"); if (columns.size() == 3 && validAttribute) { currentTableEntry = new AttributeTableEntry(tableEntryHref, name, columns.get(1), columns.get(2)); } else if (columns.size() == 4 && validAttribute) { @@ -225,10 +228,12 @@ private void handleEndOfAttributesTableRow() { columns.get(2), columns.get(3) ); - } else if (columns.size() == 2 && validInclude) { + } else if (columns.size() == 2 && validStandardInclude) { currentTableEntry = new MacroTableEntry(tableEntryHref, lastReferenceInFirstColumn, columns.get(1)); - } else if (columns.size() == 1 && validInclude) { + } else if (columns.size() == 1 && validStandardInclude) { currentTableEntry = new MacroTableEntry(tableEntryHref, lastReferenceInFirstColumn); + } else if (validFunctionalGroupMacroInclude) { + currentTableEntry = new MacroTableEntry(tableEntryHref, FUNCTIONAL_GROUP_MACRO_REFERENCE); } else { System.out.println("Invalid Row: " + Arrays.toString(columns.toArray())); return; @@ -243,31 +248,32 @@ private String extractModuleReference(String html) { if (html == null) { return null; } - String moduleRef = html.replaceAll("(?s).*?.*$", "$1"); - return moduleRef; + return html.replaceAll("(?s).*?.*$", "$1"); } private Iterable resolveMacrosRecursively(TableEntry tableEntry, Context context) { if (tableEntry instanceof MacroTableEntry macroTableEntry) { String tableId = macroTableEntry.getTableId(); - MacroTable macroTable = macroTables.get(tableId); - if (macroTable == null) { + Set matchingMacroTables = getMatchingMacroTables(tableId); + if (matchingMacroTables.isEmpty()) { System.out.println("Invalid macro key: " + tableId); return Collections.emptyList(); } MacroMetaInfo macroMetaInfo = macros.get(tableId); if (macroMetaInfo == null) { - macroMetaInfo = new MacroMetaInfo(macroTable.getTableId()); + macroMetaInfo = new MacroMetaInfo(tableId); macros.put(macroMetaInfo.getTableId(), macroMetaInfo); } - for (TableEntry macroSubEntry : macroTable.getTableEntries()) { - ContextEntry newContextEntry = new ContextEntry(macroTable.getName(), macroTable.getHref()); - // Stop at recursive macros - if (!context.getContext().contains(newContextEntry)) { - macroMetaInfo.addDataElementMetaInfo(resolveMacrosRecursively( - macroSubEntry, - new Context(newContextEntry) - )); + for (MacroTable macroTable : matchingMacroTables) { + for (TableEntry macroSubEntry : macroTable.getTableEntries()) { + ContextEntry newContextEntry = new ContextEntry(macroTable.getName(), macroTable.getHref()); + // Stop at recursive macros + if (!context.getContext().contains(newContextEntry)) { + macroMetaInfo.addDataElementMetaInfo(resolveMacrosRecursively( + macroSubEntry, + new Context(newContextEntry) + )); + } } } return macroMetaInfo.getSubDataElementMetaInfos(); @@ -303,6 +309,25 @@ private Iterable resolveMacrosRecursively(TableEntry tableE } return Collections.emptyList(); } + + // Usually only one macro matches, but there is also the Functional Groups Macros which get combined to one special macro + private Set getMatchingMacroTables(String tableId) { + Set matchingMacroTables; + if (FUNCTIONAL_GROUP_MACRO_REFERENCE.equals(tableId)) { + matchingMacroTables = macroTables.entrySet().stream() + .filter(entry -> entry.getKey().startsWith("table_C.7.6.16")) + .map(Map.Entry::getValue) + .collect(Collectors.toSet()); + } else { + MacroTable matchingMacroTable = macroTables.get(tableId); + if (matchingMacroTable == null) { + matchingMacroTables = Collections.emptySet(); + } else { + matchingMacroTables = Collections.singleton(matchingMacroTable); + } + } + return matchingMacroTables; + } private void removeHTMLTagsFromColumn(Integer i) { if (i == null || i >= this.columns.size()) { From d3ba908962fc18c740305d9333025f233241e3eb Mon Sep 17 00:00:00 2001 From: Nirostar <36939232+Nirostar@users.noreply.github.com> Date: Thu, 1 Dec 2022 11:15:48 +0100 Subject: [PATCH 5/5] Set version number to release --- README.MD | 6 +++--- gradle.properties | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index 42eccc0..ea607b3 100644 --- a/README.MD +++ b/README.MD @@ -19,7 +19,7 @@ repositories { ```groovy dependencies { - implementation 'org.dcm4che:dcm4che-typeddicom:0.4.3-SNAPSHOT' + implementation 'org.dcm4che:dcm4che-typeddicom:0.4.3' } ``` @@ -35,7 +35,7 @@ repositories { ```kotlin dependencies { - implementation("org.dcm4che:dcm4che-typeddicom:0.4.3-SNAPSHOT") + implementation("org.dcm4che:dcm4che-typeddicom:0.4.3") } ``` @@ -54,7 +54,7 @@ dependencies { org.dcm4che dcm4che-typeddicom - 0.4.3-SNAPSHOT + 0.4.3 ``` diff --git a/gradle.properties b/gradle.properties index 70f4047..e549c34 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -version=0.4.3-SNAPSHOT +version=0.4.3 org.gradle.jvmargs=-Xmx4096m org.gradle.caching=true