From 148b979796dd4f43e07bb7ab87ff1e73a2335e10 Mon Sep 17 00:00:00 2001 From: Vera Prinsen <33416829+VeraPrinsen@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:16:11 +0200 Subject: [PATCH] Remove annotations of objects that are removed in Flattened archetype (#615) * remove annotations when objects are removed because of zero occurrences * Also remove annotations for objects that are prohibited, but are not removed * Add test * add test * add tests for coverage --- .../com/nedap/archie/flattener/Flattener.java | 6 +- .../nedap/archie/flattener/FlattenerUtil.java | 33 ++++++++- .../flattener/OperationalTemplateCreator.java | 6 +- .../flattener/AnnotationsFlattenerTest.java | 69 +++++++++++++++++- ...ld_with_annotations_exclude_object.v1.adls | 71 +++++++++++++++++++ 5 files changed, 174 insertions(+), 11 deletions(-) create mode 100644 tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_child_with_annotations_exclude_object.v1.adls diff --git a/tools/src/main/java/com/nedap/archie/flattener/Flattener.java b/tools/src/main/java/com/nedap/archie/flattener/Flattener.java index 246b62693..b5d0ccc6a 100644 --- a/tools/src/main/java/com/nedap/archie/flattener/Flattener.java +++ b/tools/src/main/java/com/nedap/archie/flattener/Flattener.java @@ -253,12 +253,14 @@ private void prohibitZeroOccurrencesConstraints(Archetype archetype) { CObject object = workList.pop(); for(CAttribute attribute:object.getAttributes()) { if(attribute.getExistence() != null && attribute.getExistence().getUpper() == 0 && !attribute.getExistence().isUpperUnbounded()) { - //remove children, but do not remove attribute itself to make sure it stays prohibited + // Remove children, but do not remove attribute itself to make sure it stays prohibited + FlattenerUtil.removeAnnotationsForArchetypeConstraints(archetype, attribute.getChildren()); attribute.setChildren(new ArrayList<>()); } else { List objectsToRemove = new ArrayList<>(); for (CObject child : attribute.getChildren()) { if (!child.isAllowed()) { + FlattenerUtil.removeAnnotationsForArchetypeConstraints(archetype, List.of(child)); if(child instanceof CComplexObject) { ((CComplexObject) child).setAttributes(new ArrayList<>()); } @@ -271,9 +273,7 @@ private void prohibitZeroOccurrencesConstraints(Archetype archetype) { } attribute.getChildren().removeAll(objectsToRemove); } - } - } } diff --git a/tools/src/main/java/com/nedap/archie/flattener/FlattenerUtil.java b/tools/src/main/java/com/nedap/archie/flattener/FlattenerUtil.java index 96c4d984a..0d2824e8c 100644 --- a/tools/src/main/java/com/nedap/archie/flattener/FlattenerUtil.java +++ b/tools/src/main/java/com/nedap/archie/flattener/FlattenerUtil.java @@ -1,14 +1,15 @@ package com.nedap.archie.flattener; -import com.nedap.archie.aom.CObject; +import com.nedap.archie.aom.Archetype; +import com.nedap.archie.aom.ArchetypeConstraint; import com.nedap.archie.rules.Assertion; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; public class FlattenerUtil { - - public static List getPossiblyOverridenListValue(List parent, List child) { if(child != null && !child.isEmpty()) { return child; @@ -22,4 +23,30 @@ public static T getPossiblyOverridenValue(T parent, T specialized) { } return parent; } + + /** + * Removes annotations for the objects given, also removes annotations for children underneath the objects. + */ + public static void removeAnnotationsForArchetypeConstraints(Archetype result, List archetypeConstraints) { + if (result.getAnnotations() == null || + result.getAnnotations().getDocumentation() == null || + result.getAnnotations().getDocumentation().isEmpty() || + archetypeConstraints.isEmpty() + ) { + return; + } + + removeAnnotationsForPaths(result.getAnnotations().getDocumentation(), archetypeConstraints.stream().map(ArchetypeConstraint::getPath).collect(Collectors.toList())); + } + + private static void removeAnnotationsForPaths(Map>> annotations, List pathsToRemove) { + for (String pathToRemove : pathsToRemove) { + for (String languageKeys : annotations.keySet()) { + Map> languageAnnotations = annotations.get(languageKeys); + List toRemove = languageAnnotations.keySet().stream().filter(path -> path.startsWith(pathToRemove)).collect(Collectors.toList()); + toRemove.forEach(languageAnnotations::remove); + } + + } + } } diff --git a/tools/src/main/java/com/nedap/archie/flattener/OperationalTemplateCreator.java b/tools/src/main/java/com/nedap/archie/flattener/OperationalTemplateCreator.java index c6526d443..dd1e02f1a 100644 --- a/tools/src/main/java/com/nedap/archie/flattener/OperationalTemplateCreator.java +++ b/tools/src/main/java/com/nedap/archie/flattener/OperationalTemplateCreator.java @@ -84,10 +84,12 @@ public void removeZeroOccurrencesConstraints(Archetype archetype) { } workList.push(child); } + FlattenerUtil.removeAnnotationsForArchetypeConstraints(archetype, objectsToRemove); attribute.getChildren().removeAll(objectsToRemove); } } + FlattenerUtil.removeAnnotationsForArchetypeConstraints(archetype, attributesToRemove); object.getAttributes().removeAll(attributesToRemove); } } @@ -262,8 +264,4 @@ private void fillArchetypeRoot(CArchetypeRoot root, OperationalTemplate result) private FlattenerConfiguration getConfig() { return flattener.getConfiguration(); } - - - - } diff --git a/tools/src/test/java/com/nedap/archie/flattener/AnnotationsFlattenerTest.java b/tools/src/test/java/com/nedap/archie/flattener/AnnotationsFlattenerTest.java index a978f24e1..515b60a60 100644 --- a/tools/src/test/java/com/nedap/archie/flattener/AnnotationsFlattenerTest.java +++ b/tools/src/test/java/com/nedap/archie/flattener/AnnotationsFlattenerTest.java @@ -1,14 +1,17 @@ package com.nedap.archie.flattener; import com.nedap.archie.adlparser.ADLParser; +import com.nedap.archie.aom.Archetype; import com.nedap.archie.aom.ResourceAnnotations; import com.nedap.archie.rminfo.ReferenceModels; -import com.nedap.archie.aom.Archetype; import org.junit.Before; import org.junit.Test; import org.openehr.referencemodels.BuiltinReferenceModels; +import java.util.HashMap; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; public class AnnotationsFlattenerTest { @@ -105,4 +108,68 @@ public void flattenUsedArchetypeWithAnnotations() throws Exception { assertEquals("this is a design note on intelerance", annotations.getDocumentation().get("en").get("/data[id2]/events[id3]/data[id4]/items[id6]").get("design note")); assertEquals("this is a design note for a cluster", annotations.getDocumentation().get("en").get("/data[id2]/events[id3]/data[id4]/items[id8]/items[id3]/value[id4]").get("design note")); } + + @Test + public void flattenArchetypeWithZeroOccurrencesObject() throws Exception { + Archetype childWithExcluded = new ADLParser().parse(FlattenerTest.class.getResourceAsStream("openEHR-EHR-OBSERVATION.to_flatten_child_with_annotations_exclude_object.v1.adls")); + repository.addArchetype(childWithExcluded); + + flattener = new Flattener(repository, models).createOperationalTemplate(false); + + Archetype flattened = flattener.flatten(childWithExcluded); + assertNull(flattened.getAnnotations().getDocumentation().get("en").get("/data[id2]/events[id3]/data[id4]/items[id6]")); + } + + @Test + public void flattenArchetypeWithZeroOccurrencesObjectOPT() throws Exception { + Archetype childWithExcluded = new ADLParser().parse(FlattenerTest.class.getResourceAsStream("openEHR-EHR-OBSERVATION.to_flatten_child_with_annotations_exclude_object.v1.adls")); + repository.addArchetype(childWithExcluded); + + flattener = new Flattener(repository, models).createOperationalTemplate(true); + + Archetype flattened = flattener.flatten(childWithExcluded); + assertNull(flattened.getAnnotations().getDocumentation().get("en").get("/data[id2]/events[id3]/data[id4]/items[id6]")); + } + + @Test + public void flattenArchetypeWithNoAnnotations() throws Exception { + parent.setAnnotations(null); + repository.addArchetype(parent); + Archetype childWithExcluded = new ADLParser().parse(FlattenerTest.class.getResourceAsStream("openEHR-EHR-OBSERVATION.to_flatten_child_with_annotations_exclude_object.v1.adls")); + childWithExcluded.setAnnotations(null); + repository.addArchetype(childWithExcluded); + + flattener = new Flattener(repository, models).createOperationalTemplate(true); + + Archetype flattened = flattener.flatten(childWithExcluded); + assertNull(flattened.getAnnotations()); + } + + @Test + public void flattenArchetypeWithNoDocumentation() throws Exception { + parent.setAnnotations(new ResourceAnnotations()); + repository.addArchetype(parent); + Archetype childWithExcluded = new ADLParser().parse(FlattenerTest.class.getResourceAsStream("openEHR-EHR-OBSERVATION.to_flatten_child_with_annotations_exclude_object.v1.adls")); + childWithExcluded.setAnnotations(new ResourceAnnotations()); + repository.addArchetype(childWithExcluded); + + flattener = new Flattener(repository, models).createOperationalTemplate(true); + + Archetype flattened = flattener.flatten(childWithExcluded); + assertNull(flattened.getAnnotations().getDocumentation()); + } + + @Test + public void flattenArchetypeWithEmptyDocumentation() throws Exception { + parent.getAnnotations().setDocumentation(new HashMap<>()); + repository.addArchetype(parent); + Archetype childWithExcluded = new ADLParser().parse(FlattenerTest.class.getResourceAsStream("openEHR-EHR-OBSERVATION.to_flatten_child_with_annotations_exclude_object.v1.adls")); + childWithExcluded.getAnnotations().setDocumentation(new HashMap<>()); + repository.addArchetype(childWithExcluded); + + flattener = new Flattener(repository, models).createOperationalTemplate(true); + + Archetype flattened = flattener.flatten(childWithExcluded); + assertEquals(0, flattened.getAnnotations().getDocumentation().size()); + } } \ No newline at end of file diff --git a/tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_child_with_annotations_exclude_object.v1.adls b/tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_child_with_annotations_exclude_object.v1.adls new file mode 100644 index 000000000..46951f782 --- /dev/null +++ b/tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_child_with_annotations_exclude_object.v1.adls @@ -0,0 +1,71 @@ +archetype (adl_version=2.0.5; rm_release=1.0.2; generated) + openEHR-EHR-OBSERVATION.to_flatten_child.v1.0.0 + +specialize + openEHR-EHR-OBSERVATION.to_flatten_parent.v1 + +language + original_language = <[ISO_639-1::en]> + translations = < + ["ar-sy"] = < + language = <[ISO_639-1::ar-sy]> + author = < + ["name"] = <"Mona Saleh"> + > + > + > + +description + lifecycle_state = <"unmanaged"> + original_author = < + ["name"] = <"Heather Leslie"> + ["organisation"] = <"Ocean Informatics"> + ["email"] = <"heather.leslie@oceaninformatics.com"> + ["date"] = <"2012-12-11"> + > + copyright = <"none"> + details = < + ["en"] = < + language = <[ISO_639-1::en]> + purpose = <"specialized rules test"> + use = <"testing specialized rules"> + keywords = <"test"> + misuse = <""> + > + > + +definition + OBSERVATION[id1.1] matches { -- blood pressure + /data[id2]/events[id3]/data[id4]/items matches { + ELEMENT[id6] occurrences matches {0} + } + } + +terminology + term_definitions = < + ["en"] = < + ["id1.1"] = < + text = <"blood pressure observation"> + description = <"blood pressure observation"> + > + > + > + +annotations + documentation = < + ["en"] = < + ["/subject"] = < + ["design note"] = <"xxxxxx"> + > + ["/data[id2]/events[id3]/data[id4]/items[id5]"] = < + ["design note"] = <"this is a design note on allergic reaction, with some extra information"> + ["requirements note"] = <"this is a requirements note on allergic reaction"> + ["medline ref"] = <"this is a medline ref on allergic reaction"> + > + ["/data[id2]/events[id3]/data[id4]/items[id7]"] = < + ["design note"] = <"this is also a design note on intelerance"> + ["requirements note"] = <"this is a requirements note on intolerance"> + ["national data dictionary"] = <"NDD ref for intolerance"> + > + > + > \ No newline at end of file