diff --git a/.gitignore b/.gitignore index 1b1d8a6d9..d61b86e62 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ target/ out/ test-output/ grammars/gen +grammars/src/main/antlr/gen/ diff --git a/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/ADLListener.java b/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/ADLListener.java index e68ec823e..fdbf961a6 100644 --- a/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/ADLListener.java +++ b/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/ADLListener.java @@ -6,6 +6,7 @@ import com.nedap.archie.adlparser.antlr.AdlBaseListener; import com.nedap.archie.adlparser.antlr.AdlParser; import com.nedap.archie.adlparser.antlr.AdlParser.*; +import com.nedap.archie.aom.rmoverlay.RmOverlay; import com.nedap.archie.rminfo.MetaModels; import com.nedap.archie.serializer.odin.OdinObjectParser; import com.nedap.archie.serializer.odin.AdlOdinToJsonConverter; @@ -69,7 +70,7 @@ public void exitTemplate(TemplateContext ctx) { } @Override - public void enterTemplate_overlay(Template_overlayContext ctx) { + public void enterTemplateOverlay(TemplateOverlayContext ctx) { TemplateOverlay overlay = new TemplateOverlay(); overlay.setDifferential(true); if(rootArchetype != null) { @@ -88,7 +89,7 @@ public void enterTemplate_overlay(Template_overlayContext ctx) { } @Override - public void enterOperational_template(Operational_templateContext ctx) { + public void enterOperationalTemplate(OperationalTemplateContext ctx) { rootArchetype = new OperationalTemplate(); rootArchetype.setDifferential(false);//operational templates are flat by definition setArchetype(rootArchetype); @@ -105,7 +106,7 @@ private void parseArchetypeHRID(TerminalNode hrId) { } } - public void enterMeta_data_item(AdlParser.Meta_data_itemContext ctx) { + public void enterMetaDataItem(AdlParser.MetaDataItemContext ctx) { /* SYM_ADL_VERSION '=' VERSION_ID | SYM_UID '=' GUID @@ -119,26 +120,26 @@ public void enterMeta_data_item(AdlParser.Meta_data_itemContext ctx) { if(archetype instanceof AuthoredArchetype) { AuthoredArchetype authoredArchetype = (AuthoredArchetype) archetype; - if(ctx.meta_data_tag_adl_version() != null) { + if(ctx.metaDataTagAdlVersion() != null) { authoredArchetype.setAdlVersion(ctx.VERSION_ID().getText()); } - if(ctx.meta_data_tag_build_uid() != null) { + if(ctx.metaDataTagBuildUid() != null) { authoredArchetype.setBuildUid(ctx.GUID().getText()); } - if(ctx.meta_data_tag_rm_release() != null) { + if(ctx.metaDataTagRmRelease() != null) { authoredArchetype.setRmRelease(ctx.VERSION_ID().getText()); } - if(ctx.meta_data_tag_is_controlled() != null) { + if(ctx.metaDataTagIsControlled() != null) { authoredArchetype.setControlled(true); } - if(ctx.meta_data_tag_is_generated() != null) { + if(ctx.metaDataTagIsGenerated() != null) { authoredArchetype.setGenerated(true); } - if(ctx.meta_data_tag_uid() != null) { + if(ctx.metaDataTagUid() != null) { authoredArchetype.setUid(ctx.GUID().getText()); } else if(ctx.identifier() != null) { - authoredArchetype.addOtherMetadata(ctx.identifier().getText(), ctx.meta_data_value() == null ? null : ctx.meta_data_value().getText()); + authoredArchetype.addOtherMetadata(ctx.identifier().getText(), ctx.metaDataValue() == null ? null : ctx.metaDataValue().getText()); } } @@ -148,43 +149,49 @@ else if(ctx.identifier() != null) { * one level below: definition, language, etc. */ @Override - public void enterDefinition_section(Definition_sectionContext ctx) { + public void enterDefinitionSection(DefinitionSectionContext ctx) { CComplexObject definition = cComplexObjectParser.parseComplexObject(ctx.c_complex_object()); archetype.setDefinition(definition); } @Override - public void enterLanguage_section(Language_sectionContext ctx) { + public void enterLanguageSection(LanguageSectionContext ctx) { archetype.setAuthoredResourceContent(OdinObjectParser.convert(ctx.odin_text(), LanguageSection.class)); } @Override - public void enterTerminology_section(Terminology_sectionContext ctx) { + public void enterTerminologySection(TerminologySectionContext ctx) { archetype.setTerminology(terminologyParser.parseTerminology(ctx)); } @Override - public void enterDescription_section(AdlParser.Description_sectionContext ctx) { + public void enterDescriptionSection(AdlParser.DescriptionSectionContext ctx) { archetype.setDescription(OdinObjectParser.convert(ctx.odin_text(), ResourceDescription.class)); } @Override - public void enterSpecialization_section(Specialization_sectionContext ctx) { + public void enterSpecializationSection(SpecializationSectionContext ctx) { if(ctx != null && ctx.archetype_ref() != null) { archetype.setParentArchetypeId(ctx.archetype_ref().getText()); } } - public void enterRules_section(Rules_sectionContext ctx) { + @Override + public void enterRulesSection(RulesSectionContext ctx) { archetype.setRules(cComplexObjectParser.parseRules(ctx)); } - - public void enterAnnotations_section(AdlParser.Annotations_sectionContext ctx) { + @Override + public void enterAnnotationsSection(AdlParser.AnnotationsSectionContext ctx) { archetype.setAnnotations(OdinObjectParser.convert(ctx.odin_text(), ResourceAnnotations.class)); } - public void enterComponent_terminologies_section(AdlParser.Component_terminologies_sectionContext ctx) { + @Override + public void enterRmOverlaySection(AdlParser.RmOverlaySectionContext ctx) { + archetype.setRmOverlay(OdinObjectParser.convert(ctx.odin_text(), RmOverlay.class)); + } + + public void enterComponentTerminologiesSection(AdlParser.ComponentTerminologiesSectionContext ctx) { if (!(archetype instanceof OperationalTemplate)) { throw new IllegalArgumentException("cannot add component terminologies to anything but an operational template"); } diff --git a/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/CComplexObjectParser.java b/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/CComplexObjectParser.java index 1fb5fd98e..9b03b40cd 100644 --- a/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/CComplexObjectParser.java +++ b/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/CComplexObjectParser.java @@ -33,7 +33,7 @@ public CComplexObjectParser(ANTLRParserErrors errors, MetaModels metaModels) { this.metaModels = metaModels; } - public RulesSection parseRules(Rules_sectionContext context) { + public RulesSection parseRules(RulesSectionContext context) { RulesSection result = new RulesSection(); result.setContent(context.getText()); diff --git a/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/TerminologyParser.java b/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/TerminologyParser.java index 7274175d5..d163b0a6e 100644 --- a/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/TerminologyParser.java +++ b/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/TerminologyParser.java @@ -16,7 +16,7 @@ public TerminologyParser(ANTLRParserErrors errors) { super(errors); } - public ArchetypeTerminology parseTerminology(Terminology_sectionContext context) { + public ArchetypeTerminology parseTerminology(TerminologySectionContext context) { ArchetypeTerminology terminology = OdinObjectParser.convert(context.odin_text(), ArchetypeTerminology.class); return terminology; } diff --git a/aom/src/main/java/com/nedap/archie/aom/Archetype.java b/aom/src/main/java/com/nedap/archie/aom/Archetype.java index bec81b870..0e64a465b 100644 --- a/aom/src/main/java/com/nedap/archie/aom/Archetype.java +++ b/aom/src/main/java/com/nedap/archie/aom/Archetype.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.nedap.archie.aom.primitives.CTerminologyCode; +import com.nedap.archie.aom.rmoverlay.RmAttributeVisibility; +import com.nedap.archie.aom.rmoverlay.RmOverlay; import com.nedap.archie.aom.terminology.ArchetypeTerm; import com.nedap.archie.aom.terminology.ArchetypeTerminology; import com.nedap.archie.aom.terminology.ValueSet; @@ -10,6 +12,7 @@ import com.nedap.archie.definitions.AdlCodeDefinitions; import com.nedap.archie.query.AOMPathQuery; import com.nedap.archie.xml.adapters.ArchetypeTerminologyAdapter; +import com.nedap.archie.xml.adapters.RMOverlayXmlAdapter; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -46,7 +49,8 @@ "buildUid", "rmRelease", "generated", - "otherMetaData" + "otherMetaData", + "rmOverlay" }) public class Archetype extends AuthoredResource { @@ -75,6 +79,10 @@ public class Archetype extends AuthoredResource { //TODO: this probably requires a custom XmlAdapter private Map otherMetaData = new LinkedHashMap<>(); + @XmlElement(name="rm_overlay") + @XmlJavaTypeAdapter(RMOverlayXmlAdapter.class) + private RmOverlay rmOverlay; + public String getParentArchetypeId() { return parentArchetypeId; } @@ -274,6 +282,13 @@ public Set getAllUsedCodes() { } } + if(rmOverlay != null && rmOverlay.getRmVisibility() != null) { + for (RmAttributeVisibility value : rmOverlay.getRmVisibility().values()) { + if(value.getAlias() != null) { + result.add(value.getAlias().getCodeString()); + } + } + } return result; } @@ -333,4 +348,12 @@ public String generateNextSpecializedIdCode(String nodeId) { return nodeId + AdlCodeDefinitions.SPECIALIZATION_SEPARATOR + generateSpecializationDepthCodePrefix(specializationDepth-nodeIdSpecializationDepth-1) + (maximumIdCode+1); } + + public RmOverlay getRmOverlay() { + return rmOverlay; + } + + public void setRmOverlay(RmOverlay rmOverlay) { + this.rmOverlay = rmOverlay; + } } diff --git a/aom/src/main/java/com/nedap/archie/aom/rmoverlay/RmAttributeVisibility.java b/aom/src/main/java/com/nedap/archie/aom/rmoverlay/RmAttributeVisibility.java new file mode 100644 index 000000000..4e4405480 --- /dev/null +++ b/aom/src/main/java/com/nedap/archie/aom/rmoverlay/RmAttributeVisibility.java @@ -0,0 +1,54 @@ +package com.nedap.archie.aom.rmoverlay; + +import com.nedap.archie.aom.ArchetypeModelObject; +import com.nedap.archie.base.terminology.TerminologyCode; + +import javax.annotation.Nullable; +import javax.xml.bind.annotation.XmlType; +import java.util.Objects; + +public class RmAttributeVisibility extends ArchetypeModelObject { + + @Nullable + private VisibilityType visibility; + @Nullable + private TerminologyCode alias; + + public RmAttributeVisibility() { + } + + public RmAttributeVisibility(VisibilityType visibility, TerminologyCode alias) { + this.visibility = visibility; + this.alias = alias; + } + + public VisibilityType getVisibility() { + return visibility; + } + + public void setVisibility(VisibilityType visibility) { + this.visibility = visibility; + } + + public TerminologyCode getAlias() { + return alias; + } + + public void setAlias(TerminologyCode alias) { + this.alias = alias; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RmAttributeVisibility that = (RmAttributeVisibility) o; + return visibility == that.visibility && + Objects.equals(alias, that.alias); + } + + @Override + public int hashCode() { + return Objects.hash(visibility, alias); + } +} diff --git a/aom/src/main/java/com/nedap/archie/aom/rmoverlay/RmOverlay.java b/aom/src/main/java/com/nedap/archie/aom/rmoverlay/RmOverlay.java new file mode 100644 index 000000000..0512f9159 --- /dev/null +++ b/aom/src/main/java/com/nedap/archie/aom/rmoverlay/RmOverlay.java @@ -0,0 +1,42 @@ +package com.nedap.archie.aom.rmoverlay; + +import com.nedap.archie.aom.ArchetypeModelObject; + +import javax.annotation.Nullable; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +public class RmOverlay extends ArchetypeModelObject { + + @Nullable + Map rmVisibility = new LinkedHashMap<>(); + + public RmOverlay() { + } + + public RmOverlay(Map rmVisibility) { + this.rmVisibility = rmVisibility; + } + + public Map getRmVisibility() { + return rmVisibility; + } + + public void setRmVisibility(Map rmVisibility) { + this.rmVisibility = rmVisibility; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RmOverlay rmOverlay = (RmOverlay) o; + return Objects.equals(rmVisibility, rmOverlay.rmVisibility); + } + + @Override + public int hashCode() { + return Objects.hash(rmVisibility); + } +} diff --git a/aom/src/main/java/com/nedap/archie/aom/rmoverlay/VisibilityType.java b/aom/src/main/java/com/nedap/archie/aom/rmoverlay/VisibilityType.java new file mode 100644 index 000000000..77c6cf14d --- /dev/null +++ b/aom/src/main/java/com/nedap/archie/aom/rmoverlay/VisibilityType.java @@ -0,0 +1,15 @@ +package com.nedap.archie.aom.rmoverlay; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlEnumValue; + +public enum VisibilityType { + @JsonProperty("hide") + @XmlEnumValue("hide") + HIDE, + @JsonProperty("show") + @XmlEnumValue("show") + SHOW; +} diff --git a/aom/src/main/java/com/nedap/archie/aom/utils/AOMUtils.java b/aom/src/main/java/com/nedap/archie/aom/utils/AOMUtils.java index aff8eea67..7d142d7ec 100644 --- a/aom/src/main/java/com/nedap/archie/aom/utils/AOMUtils.java +++ b/aom/src/main/java/com/nedap/archie/aom/utils/AOMUtils.java @@ -14,7 +14,10 @@ import com.nedap.archie.definitions.AdlCodeDefinitions; import com.nedap.archie.paths.PathSegment; import com.nedap.archie.paths.PathUtil; +import com.nedap.archie.query.AOMPathQuery; import com.nedap.archie.query.APathQuery; +import com.nedap.archie.query.PartialMatch; +import com.nedap.archie.rminfo.MetaModel; import com.nedap.archie.rminfo.ModelInfoLookup; import com.nedap.archie.rminfo.RMAttributeInfo; import com.nedap.archie.rminfo.RMTypeInfo; @@ -338,6 +341,38 @@ public static int getMaximumIdCode(int specializationDepth, String prefix, Colle return maximumIdCode; } + public static boolean isPathInArchetypeOrRm(MetaModel metaModel, String path, Archetype template) { + AOMPathQuery aomPathQuery = new AOMPathQuery(path); + PartialMatch partial = aomPathQuery.findPartial(template.getDefinition()); + if(partial.isFullMatch()) { + return true; + } else { + if(isArchetypePath(partial.getRemainingPath())) { + // the remaining path is an archetype path, so cannot be found purely in the RM without + //further constraints + return false; + } + //we have a partial match left, search for it in the RM + //in case there is no match at all, getFoundObjects() will contain the root node, so this is safe + for (ArchetypeModelObject archetypeModelObject : partial.getFoundObjects()) { + if (archetypeModelObject instanceof CObject) { + if (metaModel.hasReferenceModelPath(((CObject) archetypeModelObject).getRmTypeName(), partial.getRemainingPath())) { + return true; + } + } else if (archetypeModelObject instanceof CAttribute) { + CAttribute attribute = (CAttribute) archetypeModelObject; + //matched an attribute. So if even one object matches, return true + for(CObject child:attribute.getChildren()) { + if (metaModel.hasReferenceModelPath(child.getRmTypeName(), partial.getRemainingPath())) { + return true; + } + } + } + } + } + return false; + } + /** * Given a code such as 'id4.1.0.0.1', return the nearest code that exists in a parent. In this example, * returns id4.1, so removes all zeros. diff --git a/aom/src/main/java/com/nedap/archie/query/AOMPathQuery.java b/aom/src/main/java/com/nedap/archie/query/AOMPathQuery.java index e90349133..de793409c 100644 --- a/aom/src/main/java/com/nedap/archie/query/AOMPathQuery.java +++ b/aom/src/main/java/com/nedap/archie/query/AOMPathQuery.java @@ -8,6 +8,7 @@ import com.nedap.archie.aom.CComplexObjectProxy; import com.nedap.archie.aom.CObject; import com.nedap.archie.paths.PathSegment; +import com.nedap.archie.paths.PathUtil; import java.util.ArrayList; import java.util.Collections; @@ -269,4 +270,39 @@ public List findAllMatchingPredicate(CComplexObject root, return results; } + + /** + * Find a partial match, also matching if halfway a query, including what has not yet been matched and what has not + * Does not support finding through differential paths. + * So, use on an OperationalTemplate! + * @param root the CObject to find for + * @return the partial match + */ + public PartialMatch findPartial(CComplexObject root) { + + List pathsMatched = new ArrayList<>(); + List remainingSegments = new ArrayList<>(pathSegments); + + List result = Lists.newArrayList(root); + List lastResult; + + while (!remainingSegments.isEmpty()) { + lastResult = result; + PathSegment segment = remainingSegments.remove(0); + result = findOneSegment(segment, result, false); + + if (result.size() == 0) { + //no more matches, return partial match. + //the last segment did not match anything, add it again! + remainingSegments.add(0, segment); + return new PartialMatch(lastResult, PathUtil.getPath(pathsMatched), PathUtil.getPath(remainingSegments)); + } else { + pathsMatched.add(segment); + } + } + //full match, remainingSegments is empty + return new PartialMatch(result, PathUtil.getPath(pathsMatched), PathUtil.getPath(remainingSegments)); + + + } } diff --git a/aom/src/main/java/com/nedap/archie/query/PartialMatch.java b/aom/src/main/java/com/nedap/archie/query/PartialMatch.java new file mode 100644 index 000000000..2aa216214 --- /dev/null +++ b/aom/src/main/java/com/nedap/archie/query/PartialMatch.java @@ -0,0 +1,69 @@ +package com.nedap.archie.query; + +import com.nedap.archie.aom.ArchetypeModelObject; + +import java.util.List; + +/** + * A query result of AOM Path queries that can be a partial match. Used to return query results halfway a query, so + * it returns the point where the query no longer found anything. + */ +public class PartialMatch { + private List foundObjects; + private String pathMatched; + private String remainingPath; + + public PartialMatch() { + } + + public PartialMatch(List objectFound, String pathMatched, String remainingPath) { + this.foundObjects = objectFound; + this.pathMatched = pathMatched; + this.remainingPath = remainingPath; + } + + /** + * The found objects as result of the query. Contains the root node of the document if nothing was found. + * @return The found objects as result of the query. + */ + public List getFoundObjects() { + return foundObjects; + } + + public void setFoundObjects(List foundObjects) { + this.foundObjects = foundObjects; + } + + /** + * returns whether the entire query was matched + * + * @return true if the entire query was matched, false if part of it is remaining + */ + public boolean isFullMatch() { + return remainingPath.isEmpty() || remainingPath.equals("/"); + } + + /** + * The part of the query that was matched. "/" if nothing was found. + * @return The part of the query that was matched. "/" if nothing was found. + */ + public String getPathMatched() { + return pathMatched; + } + + public void setPathMatched(String pathMatched) { + this.pathMatched = pathMatched; + } + + /** + * The remaining path in the query, that no objects matched + * @return The remaining path in the query, that no objects matched. "/" if fully found + */ + public String getRemainingPath() { + return remainingPath; + } + + public void setRemainingPath(String remainingPath) { + this.remainingPath = remainingPath; + } +} diff --git a/aom/src/main/java/com/nedap/archie/rminfo/ArchieAOMInfoLookup.java b/aom/src/main/java/com/nedap/archie/rminfo/ArchieAOMInfoLookup.java index 20216535e..34cc9492d 100644 --- a/aom/src/main/java/com/nedap/archie/rminfo/ArchieAOMInfoLookup.java +++ b/aom/src/main/java/com/nedap/archie/rminfo/ArchieAOMInfoLookup.java @@ -4,9 +4,8 @@ import com.nedap.archie.aom.ArchetypeModelObject; import com.nedap.archie.aom.CObject; import com.nedap.archie.aom.CPrimitiveObject; -import com.nedap.archie.base.Cardinality; -import com.nedap.archie.base.Interval; -import com.nedap.archie.base.terminology.TerminologyCode; +import com.nedap.archie.aom.rmoverlay.RmAttributeVisibility; +import com.nedap.archie.aom.rmoverlay.RmOverlay; import java.util.ArrayList; import java.util.Collection; @@ -100,6 +99,8 @@ protected void addTypes(Class baseClass) { addClass(com.nedap.archie.rules.Expression.class); addClass(com.nedap.archie.rules.ArchetypeIdConstraint.class); addClass(com.nedap.archie.aom.primitives.CTime.class); + addClass(RmOverlay.class); + addClass(RmAttributeVisibility.class); } @Override diff --git a/aom/src/main/java/com/nedap/archie/serializer/adl/ADLArchetypeSerializer.java b/aom/src/main/java/com/nedap/archie/serializer/adl/ADLArchetypeSerializer.java index 7d7626313..3d005ef7e 100644 --- a/aom/src/main/java/com/nedap/archie/serializer/adl/ADLArchetypeSerializer.java +++ b/aom/src/main/java/com/nedap/archie/serializer/adl/ADLArchetypeSerializer.java @@ -67,6 +67,7 @@ protected String serialize() { appendDescription(); appendDefinition(); appendRules(); + appendRmOverlay(); appendTerminology(); appendAnnotations(); @@ -89,6 +90,18 @@ protected void appendRules() { builder.newUnindentedLine(); } + protected void appendRmOverlay() { + if(archetype.getRmOverlay() == null + || archetype.getRmOverlay().getRmVisibility() == null + || archetype.getRmOverlay().getRmVisibility().isEmpty()) { + return; + } + builder.newline().append("rm_overlay").newIndentedLine() + .odin(archetype.getRmOverlay()) + .unindent(); + + } + protected void appendTerminology() { if (archetype.getTerminology() == null) return; builder.newline().append("terminology").newIndentedLine() diff --git a/aom/src/main/java/com/nedap/archie/xml/adapters/RMOverlayXmlAdapter.java b/aom/src/main/java/com/nedap/archie/xml/adapters/RMOverlayXmlAdapter.java new file mode 100644 index 000000000..9665967f6 --- /dev/null +++ b/aom/src/main/java/com/nedap/archie/xml/adapters/RMOverlayXmlAdapter.java @@ -0,0 +1,47 @@ +package com.nedap.archie.xml.adapters; + +import com.nedap.archie.aom.rmoverlay.RmOverlay; +import com.nedap.archie.aom.rmoverlay.RmAttributeVisibility; +import com.nedap.archie.xml.types.XmlRmAttributeVisibility; +import com.nedap.archie.xml.types.XmlRmOverlay; + +import javax.xml.bind.annotation.adapters.XmlAdapter; +import java.util.ArrayList; +import java.util.List; + +public class RMOverlayXmlAdapter extends XmlAdapter { + @Override + public RmOverlay unmarshal(XmlRmOverlay xmlRMOverlay) { + if(xmlRMOverlay == null) { + return null; + } + RmOverlay result = new RmOverlay(); + if(xmlRMOverlay.getRmVisibility() == null) { + return result; + } + for (XmlRmAttributeVisibility xmlVisibility : xmlRMOverlay.getRmVisibility()) { + result.getRmVisibility().put(xmlVisibility.getPath(), new RmAttributeVisibility(xmlVisibility.getVisibility(), xmlVisibility.getAlias())); + } + + return result; + } + + @Override + public XmlRmOverlay marshal(RmOverlay rmOverlay) { + if(rmOverlay == null) { + return null; + } + XmlRmOverlay result = new XmlRmOverlay(); + if(rmOverlay.getRmVisibility() == null) { + return result; + } + List resultList = new ArrayList<>(); + for (String path:rmOverlay.getRmVisibility().keySet()) { + RmAttributeVisibility rmAttributeVisibility = rmOverlay.getRmVisibility().get(path); + resultList.add(new XmlRmAttributeVisibility(path, rmAttributeVisibility.getVisibility(), rmAttributeVisibility.getAlias())); + } + result.setRmVisibility(resultList); + + return result; + } +} diff --git a/aom/src/main/java/com/nedap/archie/xml/types/XmlRmAttributeVisibility.java b/aom/src/main/java/com/nedap/archie/xml/types/XmlRmAttributeVisibility.java new file mode 100644 index 000000000..69bfa00c7 --- /dev/null +++ b/aom/src/main/java/com/nedap/archie/xml/types/XmlRmAttributeVisibility.java @@ -0,0 +1,56 @@ +package com.nedap.archie.xml.types; + +import com.nedap.archie.aom.rmoverlay.VisibilityType; +import com.nedap.archie.base.terminology.TerminologyCode; + +import javax.annotation.Nullable; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlType; + +@XmlType(name="RM_ATTRIBUTE_VISIBILITY") +@XmlAccessorType(XmlAccessType.FIELD) +public class XmlRmAttributeVisibility { + + private String path; + @Nullable + private VisibilityType visibility; + @Nullable + private TerminologyCode alias; + + public XmlRmAttributeVisibility() { + } + + public XmlRmAttributeVisibility(String path, @Nullable VisibilityType visibility, @Nullable TerminologyCode alias) { + this.path = path; + this.visibility = visibility; + this.alias = alias; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + @Nullable + public VisibilityType getVisibility() { + return visibility; + } + + public void setVisibility(@Nullable VisibilityType visibility) { + this.visibility = visibility; + } + + @Nullable + public TerminologyCode getAlias() { + return alias; + } + + public void setAlias(@Nullable TerminologyCode alias) { + this.alias = alias; + } +} diff --git a/aom/src/main/java/com/nedap/archie/xml/types/XmlRmOverlay.java b/aom/src/main/java/com/nedap/archie/xml/types/XmlRmOverlay.java new file mode 100644 index 000000000..f2b7d2c0a --- /dev/null +++ b/aom/src/main/java/com/nedap/archie/xml/types/XmlRmOverlay.java @@ -0,0 +1,30 @@ +package com.nedap.archie.xml.types; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; +import java.util.List; + + +@XmlType(name="RM_OVERLAY") +@XmlAccessorType(XmlAccessType.FIELD) +public class XmlRmOverlay { + @XmlElement(name="rm_visibility") + private List rmVisibility; + + public XmlRmOverlay() { + } + + public XmlRmOverlay(List visibility) { + this.rmVisibility = visibility; + } + + public List getRmVisibility() { + return rmVisibility; + } + + public void setRmVisibility(List rmVisibility) { + this.rmVisibility = rmVisibility; + } +} diff --git a/aom/src/test/java/com/nedap/archie/aom/utils/AOMUtilsTest.java b/aom/src/test/java/com/nedap/archie/aom/utils/AOMUtilsTest.java index 19161bbbb..cdf6b45ab 100644 --- a/aom/src/test/java/com/nedap/archie/aom/utils/AOMUtilsTest.java +++ b/aom/src/test/java/com/nedap/archie/aom/utils/AOMUtilsTest.java @@ -14,4 +14,5 @@ public void codeAtLevel() { assertEquals("id1.1", AOMUtils.codeAtLevel("id1.1.1", 1)); assertEquals("id1", AOMUtils.codeAtLevel("id1.0.1", 1)); } + } diff --git a/archie-utils/src/main/java/com/nedap/archie/json/JacksonUtil.java b/archie-utils/src/main/java/com/nedap/archie/json/JacksonUtil.java index 784a155e8..b9133a872 100644 --- a/archie-utils/src/main/java/com/nedap/archie/json/JacksonUtil.java +++ b/archie-utils/src/main/java/com/nedap/archie/json/JacksonUtil.java @@ -19,10 +19,12 @@ import com.nedap.archie.base.OpenEHRBase; import com.nedap.archie.rm.archetyped.Pathable; import com.nedap.archie.rm.support.identification.ArchetypeID; +import com.nedap.archie.rminfo.ArchieAOMInfoLookup; import com.nedap.archie.rminfo.ArchieRMInfoLookup; import com.nedap.archie.rminfo.RMTypeInfo; import java.io.IOException; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -145,7 +147,8 @@ public ArchieTypeResolverBuilder(RMJacksonConfiguration configuration) .allowIfBaseType(OpenEHRBase.class).build()); classesToNotAddTypeProperty = new HashSet<>(); if(!configuration.isAlwaysIncludeTypeProperty()) { - List allTypes = ArchieRMInfoLookup.getInstance().getAllTypes(); + List allTypes = new ArrayList<>(ArchieRMInfoLookup.getInstance().getAllTypes()); + allTypes.addAll(ArchieAOMInfoLookup.getInstance().getAllTypes()); for(RMTypeInfo type:allTypes) { if(type.getDirectDescendantClasses().isEmpty()) { classesToNotAddTypeProperty.add(type.getJavaClass()); diff --git a/grammars/src/main/antlr/Adl.g4 b/grammars/src/main/antlr/Adl.g4 index 337160e35..ff609c654 100755 --- a/grammars/src/main/antlr/Adl.g4 +++ b/grammars/src/main/antlr/Adl.g4 @@ -13,84 +13,89 @@ import cadl, odin; // ============== Parser rules ============== // -adl: ( archetype | template | template_overlay | operational_template ) EOF ; +adl: ( archetype | template | templateOverlay | operationalTemplate ) EOF ; archetype: - SYM_ARCHETYPE meta_data? + SYM_ARCHETYPE metaData? ARCHETYPE_HRID - specialization_section? - language_section - description_section - definition_section - rules_section? - terminology_section - annotations_section? + specializationSection? + languageSection + descriptionSection + definitionSection + rulesSection? + rmOverlaySection? + terminologySection + annotationsSection? ; template: - SYM_TEMPLATE meta_data? + SYM_TEMPLATE metaData? ARCHETYPE_HRID - specialization_section - language_section - description_section - definition_section - rules_section? - terminology_section - annotations_section? - (template_overlay)* + specializationSection + languageSection + descriptionSection + definitionSection + rulesSection? + rmOverlaySection? + terminologySection + annotationsSection? + (templateOverlay)* ; -template_overlay: +templateOverlay: SYM_TEMPLATE_OVERLAY //this token includes the horizontal comment line ARCHETYPE_HRID - specialization_section - definition_section - terminology_section + specializationSection + definitionSection + rmOverlaySection? + terminologySection ; -operational_template: - SYM_OPERATIONAL_TEMPLATE meta_data? +operationalTemplate: + SYM_OPERATIONAL_TEMPLATE metaData? ARCHETYPE_HRID - specialization_section? - language_section - description_section - definition_section - rules_section? - terminology_section - annotations_section? - component_terminologies_section? + specializationSection? + languageSection + descriptionSection + definitionSection + rulesSection? + rmOverlaySection? + terminologySection + annotationsSection? + componentTerminologiesSection? ; -specialization_section : SYM_SPECIALIZE archetype_ref ; -language_section : SYM_LANGUAGE odin_text ; -description_section : SYM_DESCRIPTION odin_text ; -definition_section : SYM_DEFINITION c_complex_object ; -rules_section : SYM_RULES assertion_list; -terminology_section : SYM_TERMINOLOGY odin_text ; -annotations_section : SYM_ANNOTATIONS odin_text ; -component_terminologies_section: SYM_COMPONENT_TERMINOLOGIES odin_text ; +specializationSection : SYM_SPECIALIZE archetype_ref ; +languageSection : SYM_LANGUAGE odin_text ; +descriptionSection : SYM_DESCRIPTION odin_text ; +definitionSection : SYM_DEFINITION c_complex_object ; +rulesSection : SYM_RULES assertion_list; +terminologySection : SYM_TERMINOLOGY odin_text ; +annotationsSection : SYM_ANNOTATIONS odin_text ; +rmOverlaySection : SYM_RM_OVERLAY odin_text ; +componentTerminologiesSection: SYM_COMPONENT_TERMINOLOGIES odin_text ; -meta_data: '(' meta_data_item (';' meta_data_item )* ')' ; +metaData: '(' metaDataItem (';' metaDataItem )* ')' ; -meta_data_item: - meta_data_tag_adl_version '=' VERSION_ID - | meta_data_tag_uid '=' GUID - | meta_data_tag_build_uid '=' GUID - | meta_data_tag_rm_release '=' VERSION_ID - | meta_data_tag_is_controlled - | meta_data_tag_is_generated - | identifier ( '=' meta_data_value )? +metaDataItem: + metaDataTagAdlVersion '=' VERSION_ID + | metaDataTagUid '=' GUID + | metaDataTagBuildUid '=' GUID + | metaDataTagRmRelease '=' VERSION_ID + | metaDataTagIsControlled + | metaDataTagIsGenerated + | identifier ( '=' metaDataValue )? ; -meta_data_value: +metaDataValue: primitive_value | GUID | VERSION_ID ; -meta_data_tag_adl_version : 'adl_version' ; -meta_data_tag_uid : 'uid' ; -meta_data_tag_build_uid : 'build_uid' ; -meta_data_tag_rm_release : 'rm_release' ; -meta_data_tag_is_controlled : 'controlled' ; -meta_data_tag_is_generated : 'generated' ; +metaDataTagAdlVersion : 'adl_version' ; +metaDataTagUid : 'uid' ; +metaDataTagBuildUid : 'build_uid' ; +metaDataTagRmRelease : 'rm_release' ; +metaDataTagIsControlled : 'controlled' ; +metaDataTagIsGenerated : 'generated' ; diff --git a/grammars/src/main/antlr/adl_keywords.g4 b/grammars/src/main/antlr/adl_keywords.g4 index 17c970ba5..3d56704dc 100755 --- a/grammars/src/main/antlr/adl_keywords.g4 +++ b/grammars/src/main/antlr/adl_keywords.g4 @@ -20,6 +20,7 @@ SYM_DEFINITION : '\n'[Dd][Ee][Ff][Ii][Nn][Ii][Tt][Ii][Oo][Nn] ; SYM_RULES : '\n'[Rr][Uu][Ll][Ee][Ss] ; SYM_TERMINOLOGY : '\n'[Tt][Ee][Rr][Mm][Ii][Nn][Oo][Ll][Oo][Gg][Yy] ; SYM_ANNOTATIONS : '\n'[Aa][Nn][Nn][Oo][Tt][Aa][Tt][Ii][Oo][Nn][Ss] ; +SYM_RM_OVERLAY : '\n'[Rr][Mm]'_'[Oo][Vv][Ee][Rr][Ll][Aa][Yy] ; SYM_COMPONENT_TERMINOLOGIES : '\n'[Cc][Oo][Mm][Pp][Oo][Nn][Ee][Nn][Tt]'_'[Tt][Ee][Rr][Mm][Ii][Nn][Oo][Ll][Oo][Gg][Ii][Ee][Ss] ; // CADL keywords diff --git a/tools/src/main/java/com/nedap/archie/archetypevalidator/ArchetypeValidator.java b/tools/src/main/java/com/nedap/archie/archetypevalidator/ArchetypeValidator.java index fbf0dfefc..20fbda121 100644 --- a/tools/src/main/java/com/nedap/archie/archetypevalidator/ArchetypeValidator.java +++ b/tools/src/main/java/com/nedap/archie/archetypevalidator/ArchetypeValidator.java @@ -74,6 +74,7 @@ public ArchetypeValidator(MetaModels models) { validationsPhase3 = new ArrayList<>(); validationsPhase3.add(new AnnotationsValidation()); + validationsPhase3.add(new RmOverlayValidation()); validationsPhase3.add(new FlatFormValidation()); } diff --git a/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/AnnotationsValidation.java b/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/AnnotationsValidation.java index f6b3d0877..122d4a2ad 100644 --- a/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/AnnotationsValidation.java +++ b/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/AnnotationsValidation.java @@ -3,18 +3,14 @@ import com.nedap.archie.aom.Archetype; import com.nedap.archie.aom.AuthoredArchetype; -import com.nedap.archie.aom.OperationalTemplate; import com.nedap.archie.aom.ResourceAnnotations; import com.nedap.archie.aom.utils.AOMUtils; import com.nedap.archie.archetypevalidator.ArchetypeValidationBase; import com.nedap.archie.archetypevalidator.ErrorType; -import com.nedap.archie.query.AOMPathQuery; import org.openehr.utils.message.I18n; import java.util.Map; - - public class AnnotationsValidation extends ArchetypeValidationBase { public AnnotationsValidation() { @@ -29,8 +25,6 @@ public void validate() { for(String language: annotations.getDocumentation().keySet()) { Map> annotationsForLanguage = annotations.getDocumentation().get(language); for(String path: annotationsForLanguage.keySet()) { - Map annotationsForPath = annotationsForLanguage.get(path); - boolean isArchetypePath = AOMUtils.isArchetypePath(path) ; //TODO: NO idea what the eiffel code here suggests it does //we need the operational template to look up paths across included archetypes //otherwise any path crossing an ARCHETYPE_ROOT will fail to lookup Archetype operationalTemplate = repository.getOperationalTemplate(archetype.getArchetypeId().toString()); @@ -38,32 +32,13 @@ public void validate() { //apparently the operational template creation failed. Try to lookup the path anyway in the original archetype operationalTemplate = archetype; } - if(isArchetypePath) { - if(!(hasPath(path, operationalTemplate) || (flatParent != null && hasPath(path, flatParent)))) { - addMessage(ErrorType.VRANP, I18n.t("The path {0} referenced in the annotations does not exist in the flat archetype", path)); - } - } else { //TODO: this can also be referencemodel.has_path, but that's not implemented yet - if(!combinedModels.hasReferenceModelPath(archetype.getDefinition().getRmTypeName(), path)) { - addMessage(ErrorType.VRANP, I18n.t("The path {0} referenced in the annotations does not exist in the flat archetype or reference model", path)); - } + if(!AOMUtils.isPathInArchetypeOrRm(combinedModels.getSelectedModel(), path, operationalTemplate)) { + addMessage(ErrorType.VRANP, I18n.t("The path {0} referenced in the annotations does not exist in the flat archetype or reference model", path)); } - } - } } } - - } - /** - * Check if the archetype has this path directly, or a specialized variant of this path - * @param path the path to check - * @param archetype the archetype to find the path in - * @return true if the archetype has the path, false if it does not - */ - private boolean hasPath(String path, Archetype archetype) { - return ! new AOMPathQuery(path).findList(archetype.getDefinition(), true).isEmpty(); - } } diff --git a/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/RmOverlayValidation.java b/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/RmOverlayValidation.java new file mode 100644 index 000000000..5f618e33b --- /dev/null +++ b/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/RmOverlayValidation.java @@ -0,0 +1,48 @@ +package com.nedap.archie.archetypevalidator.validations; + +import com.nedap.archie.aom.Archetype; +import com.nedap.archie.aom.AuthoredArchetype; +import com.nedap.archie.aom.rmoverlay.RmOverlay; +import com.nedap.archie.aom.utils.AOMUtils; +import com.nedap.archie.archetypevalidator.ArchetypeValidationBase; +import com.nedap.archie.archetypevalidator.ErrorType; +import com.nedap.archie.base.terminology.TerminologyCode; +import org.openehr.utils.message.I18n; + +public class RmOverlayValidation extends ArchetypeValidationBase { + + public RmOverlayValidation() { + super(); + } + + @Override + public void validate() { + if(archetype instanceof AuthoredArchetype) { + if(archetype.getRmOverlay() != null) { + RmOverlay rmOverlay = archetype.getRmOverlay(); + if(rmOverlay.getRmVisibility() != null) { + for(String path:rmOverlay.getRmVisibility().keySet()) { + + //we need the operational template to look up paths across included archetypes + //otherwise any path crossing an ARCHETYPE_ROOT will fail to lookup + Archetype operationalTemplate = repository.getOperationalTemplate(archetype.getArchetypeId().toString()); + if(operationalTemplate == null) { + //apparently the operational template creation failed. Try to lookup the path anyway in the original archetype + operationalTemplate = archetype; + } + if(!AOMUtils.isPathInArchetypeOrRm(combinedModels.getSelectedModel(), path, operationalTemplate)) { + addMessage(ErrorType.VRANP, I18n.t("The path {0} referenced in the rm visibility does not exist in the flat archetype", path)); + } + + TerminologyCode alias = rmOverlay.getRmVisibility().get(path).getAlias(); + if(alias != null && alias.getCodeString() != null && AOMUtils.isValueCode(alias.getCodeString())) { + if(operationalTemplate.getTerm(archetype.getDefinition(), alias.getCodeString(), archetype.getOriginalLanguage().getCodeString()) == null) { + addMessage(ErrorType.VATID, I18n.t("The code {0} is missing in the terminology. It is defined in rm_visibility at path {1}", alias.getCodeString(), path)); + } + } + } + } + } + } + } +} diff --git a/tools/src/main/java/com/nedap/archie/flattener/AnnotationsFlattener.java b/tools/src/main/java/com/nedap/archie/flattener/AnnotationsAndOverlaysFlattener.java similarity index 63% rename from tools/src/main/java/com/nedap/archie/flattener/AnnotationsFlattener.java rename to tools/src/main/java/com/nedap/archie/flattener/AnnotationsAndOverlaysFlattener.java index 728183d7b..28a9cd1c6 100644 --- a/tools/src/main/java/com/nedap/archie/flattener/AnnotationsFlattener.java +++ b/tools/src/main/java/com/nedap/archie/flattener/AnnotationsAndOverlaysFlattener.java @@ -3,13 +3,15 @@ import com.nedap.archie.aom.Archetype; import com.nedap.archie.aom.OperationalTemplate; import com.nedap.archie.aom.ResourceAnnotations; +import com.nedap.archie.aom.rmoverlay.RmOverlay; +import com.nedap.archie.aom.rmoverlay.RmAttributeVisibility; import java.util.LinkedHashMap; import java.util.Map; -public class AnnotationsFlattener { +public class AnnotationsAndOverlaysFlattener { - public void flatten(Archetype parent, Archetype child, Archetype result) { + public void flattenAnnotations(Archetype parent, Archetype child, Archetype result) { if( (isAnnotationsEmpty(parent) && isAnnotationsEmpty(child))) { return; } @@ -20,6 +22,90 @@ public void flatten(Archetype parent, Archetype child, Archetype result) { mergeInAnnotations(child, resultDocumentation); } + public void flattenRmOverlay(Archetype parent, Archetype child, Archetype result) { + if( (isRmOverlayEmpty(parent) && isRmOverlayEmpty(child))) { + return; + } + RmOverlay resultOverlay = ensureRmOverlayPresent(result); + Map resultVisibility = resultOverlay.getRmVisibility(); + + mergeInVisibility(parent, resultVisibility); + mergeInVisibility(child, resultVisibility); + } + + public void addVisibilityWithPathPrefix(String pathPrefix, Archetype archetype, OperationalTemplate result) { + if(isRmOverlayEmpty(archetype)) { + return; + } + ensureRmOverlayPresent(result); + + Map rmVisibilityToBeMergedIn = archetype.getRmOverlay().getRmVisibility(); + + for(String path: rmVisibilityToBeMergedIn.keySet()) { + String newPath = ensureNoSlashAtEnd(pathPrefix) + path; + result.getRmOverlay().getRmVisibility().put(newPath, (RmAttributeVisibility) rmVisibilityToBeMergedIn.get(path).clone()); + } + } + + public void addAnnotationsWithPathPrefix(String pathPrefix, Archetype archetype, OperationalTemplate result) { + if(isAnnotationsEmpty(archetype)) { + return; + } + Map>> documentationToBeMergedIn = archetype.getAnnotations().getDocumentation(); + for(String language: documentationToBeMergedIn.keySet()) { + + Map> languageAnnotationsToBeMergedIn = documentationToBeMergedIn.get(language); + for(String path: languageAnnotationsToBeMergedIn.keySet()) { + String newPath = ensureNoSlashAtEnd(pathPrefix) + path; + merge(language, newPath, languageAnnotationsToBeMergedIn.get(path), result); + } + } + } + + /* visibility private methods */ + private void mergeInVisibility(Archetype toBeMergedIn, Map resultVisibility) { + if(!isRmOverlayEmpty(toBeMergedIn)) { + RmOverlay toBeMergedInRmOverlay = toBeMergedIn.getRmOverlay(); + Map toBeMergedInRmVisibility = toBeMergedInRmOverlay.getRmVisibility(); + mergeVisibility(resultVisibility, toBeMergedInRmVisibility); + } + } + + private void mergeVisibility(Map resultVisibility, Map toBeMergedInRmVisibility) { + for(String path:toBeMergedInRmVisibility.keySet()) { + RmAttributeVisibility toBeMergedInVisibility = toBeMergedInRmVisibility.get(path); + RmAttributeVisibility targetVisibility = resultVisibility.get(path); + if(targetVisibility == null) { + targetVisibility = (RmAttributeVisibility) toBeMergedInVisibility.clone(); + resultVisibility.put(path, targetVisibility); + } else { + //two visibilities. One should override the other? + targetVisibility = (RmAttributeVisibility) toBeMergedInVisibility.clone(); + resultVisibility.put(path, targetVisibility); + } + } + } + + private RmOverlay ensureRmOverlayPresent(Archetype result) { + if(result.getRmOverlay() == null) { + result.setRmOverlay(new RmOverlay()); + } + if(result.getRmOverlay().getRmVisibility() == null) { + result.getRmOverlay().setRmVisibility(new LinkedHashMap<>()); + } + return result.getRmOverlay(); + } + + private boolean isRmOverlayEmpty(Archetype archetype) { + return archetype.getRmOverlay() == null + || archetype.getRmOverlay().getRmVisibility() == null + || archetype.getRmOverlay().getRmVisibility().isEmpty(); + } + + + + /* annotations private methods */ + private ResourceAnnotations ensureAnnotationsPresent(Archetype result) { ResourceAnnotations resultAnnotation = result.getAnnotations(); if(resultAnnotation == null) { @@ -81,22 +167,6 @@ private boolean isAnnotationsEmpty(Archetype archetype) { archetype.getAnnotations().getDocumentation().isEmpty(); } - public void addAnnotationsWithPathPrefix(String pathPrefix, Archetype archetype, OperationalTemplate result) { - if(isAnnotationsEmpty(archetype)) { - return; - } - Map>> documentationToBeMergedIn = archetype.getAnnotations().getDocumentation(); - for(String language: documentationToBeMergedIn.keySet()) { - - Map> languageAnnotationsToBeMergedIn = documentationToBeMergedIn.get(language); - for(String path: languageAnnotationsToBeMergedIn.keySet()) { - String newPath = ensureNoSlashAtEnd(pathPrefix) + path; - merge(language, newPath, languageAnnotationsToBeMergedIn.get(path), result); - } - } - - } - private void merge(String language, String newPath, Map annotationsMap, OperationalTemplate result) { ResourceAnnotations annotations = ensureAnnotationsPresent(result); Map> languageMap = annotations.getDocumentation().get(language); @@ -113,6 +183,7 @@ private void merge(String language, String newPath, Map annotati } } + /* shared private methods */ private String ensureNoSlashAtEnd(String pathPrefix) { if(pathPrefix.endsWith("/")) { return pathPrefix.substring(0, pathPrefix.length() - 1); 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 ab474ceac..5cabc6283 100644 --- a/tools/src/main/java/com/nedap/archie/flattener/Flattener.java +++ b/tools/src/main/java/com/nedap/archie/flattener/Flattener.java @@ -32,7 +32,7 @@ public class Flattener implements IAttributeFlattenerSupport { private final FlattenerConfiguration config; private RulesFlattener rulesFlattener = new RulesFlattener(); - private AnnotationsFlattener annotationsFlattener = new AnnotationsFlattener(); + private AnnotationsAndOverlaysFlattener annotationsAndOverlaysFlattener = new AnnotationsAndOverlaysFlattener(); CAttributeFlattener cAttributeFlattener = new CAttributeFlattener(this); private TupleFlattener tupleFlattener = new TupleFlattener(); @@ -165,7 +165,8 @@ public Archetype flatten(Archetype toFlatten) { result.setTerminology(clonedParent.getTerminology()); result.setRules(clonedParent.getRules()); } - new AnnotationsFlattener().flatten(parent, child, result); + annotationsAndOverlaysFlattener.flattenAnnotations(parent, child, result); + annotationsAndOverlaysFlattener.flattenRmOverlay(parent, child, result); //1. redefine structure //2. fill archetype slots if we are creating an operational template @@ -444,7 +445,7 @@ protected RulesFlattener getRulesFlattener() { return rulesFlattener; } - protected AnnotationsFlattener getAnnotationsFlattener() { return annotationsFlattener; } + protected AnnotationsAndOverlaysFlattener getAnnotationsAndOverlaysFlattener() { return annotationsAndOverlaysFlattener; } public OverridingArchetypeRepository getRepository() { return repository; 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 43ce6b0da..74b607ee0 100644 --- a/tools/src/main/java/com/nedap/archie/flattener/OperationalTemplateCreator.java +++ b/tools/src/main/java/com/nedap/archie/flattener/OperationalTemplateCreator.java @@ -237,7 +237,8 @@ private void fillArchetypeRoot(CArchetypeRoot root, OperationalTemplate result) String prefix = archetype.getArchetypeId().getConceptId() + "_"; flattener.getRulesFlattener().combineRules(archetype, root.getArchetype(), prefix, prefix, rootToFill.getPath(), false); - flattener.getAnnotationsFlattener().addAnnotationsWithPathPrefix(rootToFill.getPath(), archetype, result); + flattener.getAnnotationsAndOverlaysFlattener().addAnnotationsWithPathPrefix(rootToFill.getPath(), archetype, result); + flattener.getAnnotationsAndOverlaysFlattener().addVisibilityWithPathPrefix(rootToFill.getPath(), archetype, result); //todo: do we have to put something in the terminology extracts? //templateResult.addTerminologyExtract(child.getNodeId(), archetype.getTerminology().); } diff --git a/tools/src/test/java/com/nedap/archie/adlparser/AOMPathQueryTest.java b/tools/src/test/java/com/nedap/archie/adlparser/AOMPathQueryTest.java index ccfd2c641..c90e6b392 100644 --- a/tools/src/test/java/com/nedap/archie/adlparser/AOMPathQueryTest.java +++ b/tools/src/test/java/com/nedap/archie/adlparser/AOMPathQueryTest.java @@ -7,6 +7,7 @@ import com.nedap.archie.aom.CComplexObject; import com.nedap.archie.aom.primitives.CReal; import com.nedap.archie.query.AOMPathQuery; +import com.nedap.archie.query.PartialMatch; import com.nedap.archie.testutil.TestUtil; import org.junit.After; import org.junit.Before; @@ -117,4 +118,38 @@ public void findOneMatchingObject() { precision = dvQuantity.itemAtPath("/precision[1]"); assertNull(precision); } + + @Test + public void findPartial() { + String queryString = "/context[id11]/other_context[id2]/items[qualification]/items[4]/value[1]"; + AOMPathQuery query = new AOMPathQuery(queryString); + PartialMatch fullMatch = query.findPartial(archetype.getDefinition()); + assertTrue("this should be a full match", fullMatch.isFullMatch()); + assertEquals("only one object should be found", 1, fullMatch.getFoundObjects().size()); + assertEquals("the matched string should be equal to the query", queryString, fullMatch.getPathMatched()); + assertEquals("the remainder should be '/'", "/", fullMatch.getRemainingPath()); + + queryString = "/context[id11]/health_care_facility/name"; + query = new AOMPathQuery(queryString); + PartialMatch partialMatch = query.findPartial(archetype.getDefinition()); + assertFalse("this should be a partial match", partialMatch.isFullMatch()); + assertEquals("only one object should be found", 1, partialMatch.getFoundObjects().size()); + assertEquals(archetype.getDefinition().getAttribute("context").getChildren().get(0), + partialMatch.getFoundObjects().get(0)); + assertEquals("the matched string should be equal to the first part of the query", + "/context[id11]", partialMatch.getPathMatched()); + assertEquals("the remainder should contain the last part", + "/health_care_facility/name", partialMatch.getRemainingPath()); + + queryString = "/non_existing_path/with_extras"; + query = new AOMPathQuery(queryString); + PartialMatch noMatch = query.findPartial(archetype.getDefinition()); + assertFalse("this should be a partial match", noMatch.isFullMatch()); + assertEquals("the root node should be found", 1, noMatch.getFoundObjects().size()); + assertEquals(archetype.getDefinition(), noMatch.getFoundObjects().get(0)); + assertEquals("the matched string should be empty", + "/", noMatch.getPathMatched()); + assertEquals("the remainder should contain the full query", + queryString, noMatch.getRemainingPath()); + } } diff --git a/tools/src/test/java/com/nedap/archie/adlparser/AomUtilsPathFindingTest.java b/tools/src/test/java/com/nedap/archie/adlparser/AomUtilsPathFindingTest.java new file mode 100644 index 000000000..149e5b259 --- /dev/null +++ b/tools/src/test/java/com/nedap/archie/adlparser/AomUtilsPathFindingTest.java @@ -0,0 +1,29 @@ +package com.nedap.archie.adlparser; + +import com.nedap.archie.aom.Archetype; +import com.nedap.archie.aom.utils.AOMUtils; +import com.nedap.archie.rminfo.MetaModels; +import com.nedap.archie.testutil.TestUtil; +import org.junit.Test; +import org.openehr.referencemodels.BuiltinReferenceModels; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class AomUtilsPathFindingTest { + + @Test + public void isPathInArchetypeOrRm() throws Exception{ + Archetype archetype = TestUtil.parseFailOnErrors("/basic.adl"); + MetaModels metaModels = BuiltinReferenceModels.getMetaModels(); + metaModels.selectModel(archetype); + //AOM path + assertTrue(AOMUtils.isPathInArchetypeOrRm(metaModels.getSelectedModel(), "/context[id11]", archetype)); + //mixed aom + RM path + assertTrue(AOMUtils.isPathInArchetypeOrRm(metaModels.getSelectedModel(), "/context[id11]/health_care_facility/name", archetype)); + //path not in AOM, only in RM + assertTrue(AOMUtils.isPathInArchetypeOrRm(metaModels.getSelectedModel(), "/composer/external_ref", archetype)); + //non-existing path + assertFalse(AOMUtils.isPathInArchetypeOrRm(metaModels.getSelectedModel(), "/doesnot_exists", archetype)); + } +} diff --git a/tools/src/test/java/com/nedap/archie/flattener/RMOverlayFlattenerTest.java b/tools/src/test/java/com/nedap/archie/flattener/RMOverlayFlattenerTest.java new file mode 100644 index 000000000..f297c67a5 --- /dev/null +++ b/tools/src/test/java/com/nedap/archie/flattener/RMOverlayFlattenerTest.java @@ -0,0 +1,49 @@ +package com.nedap.archie.flattener; + +import com.nedap.archie.aom.Archetype; +import com.nedap.archie.aom.OperationalTemplate; +import com.nedap.archie.aom.rmoverlay.VisibilityType; +import com.nedap.archie.archetypevalidator.ArchetypeValidator; +import com.nedap.archie.testutil.TestUtil; +import org.junit.Test; +import org.openehr.referencemodels.BuiltinReferenceModels; + +import static org.junit.Assert.*; + +public class RMOverlayFlattenerTest { + + @Test + public void flattenParentChild() throws Exception{ + Archetype parent = TestUtil.parseFailOnErrors("/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1.0.0.adls"); + Archetype child = TestUtil.parseFailOnErrors("/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_child_with_overlay.v1.0.0.adls"); + InMemoryFullArchetypeRepository repository = new InMemoryFullArchetypeRepository(); + repository.addArchetype(parent); + repository.addArchetype(child); + repository.compile(new ArchetypeValidator(BuiltinReferenceModels.getMetaModels())); + + repository.getAllValidationResults().forEach(v -> assertTrue(v.getErrors().toString(), !v.hasWarningsOrErrors())); + Archetype flattenedChild = repository.getFlattenedArchetype("openEHR-EHR-OBSERVATION.to_flatten_child_with_overlay.v1.0.0"); + assertEquals(VisibilityType.HIDE, parent.getRmOverlay().getRmVisibility().get("/subject").getVisibility()); + assertNull(parent.getRmOverlay().getRmVisibility().get("/state")); + assertEquals(VisibilityType.HIDE, flattenedChild.getRmOverlay().getRmVisibility().get("/subject").getVisibility()); + assertEquals("at12", flattenedChild.getRmOverlay().getRmVisibility().get("/subject").getAlias().getCodeString()); + assertEquals("local", flattenedChild.getRmOverlay().getRmVisibility().get("/subject").getAlias().getTerminologyIdString()); + assertEquals(VisibilityType.HIDE, flattenedChild.getRmOverlay().getRmVisibility().get("/state").getVisibility()); + + } + + @Test + public void flattenIncludedArchetype() throws Exception { + Archetype parent = TestUtil.parseFailOnErrors("/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1.0.0.adls"); + Archetype composition = TestUtil.parseFailOnErrors("/com/nedap/archie/flattener/openEHR-COMPOSITION.including_overlay.v1.0.0.adls"); + InMemoryFullArchetypeRepository repository = new InMemoryFullArchetypeRepository(); + repository.addArchetype(parent); + repository.addArchetype(composition); + repository.compile(new ArchetypeValidator(BuiltinReferenceModels.getMetaModels())); + + repository.getAllValidationResults().forEach(v -> assertTrue(v.getErrors().toString(), !v.hasWarningsOrErrors())); + OperationalTemplate opt = (OperationalTemplate) new Flattener(repository, BuiltinReferenceModels.getMetaModels(), FlattenerConfiguration.forOperationalTemplate()).flatten(composition); + assertEquals(VisibilityType.HIDE, opt.getRmOverlay().getRmVisibility().get("/content[id2]/subject").getVisibility()); + assertEquals("at12", opt.getRmOverlay().getRmVisibility().get("/content[id2]/subject").getAlias().getCodeString()); + } +} diff --git a/tools/src/test/java/com/nedap/archie/flattener/specexamples/FlattenerTestUtil.java b/tools/src/test/java/com/nedap/archie/flattener/specexamples/FlattenerTestUtil.java index 094c3ee88..c5896b99b 100644 --- a/tools/src/test/java/com/nedap/archie/flattener/specexamples/FlattenerTestUtil.java +++ b/tools/src/test/java/com/nedap/archie/flattener/specexamples/FlattenerTestUtil.java @@ -15,12 +15,13 @@ public class FlattenerTestUtil { public static Archetype parse(String filename) throws IOException, ADLParseException { ADLParser parser = new ADLParser(); - InputStream stream = FlattenerTestUtil.class.getResourceAsStream(filename); - if(stream == null) { - fail("cannot find file: " + filename); + try(InputStream stream = FlattenerTestUtil.class.getResourceAsStream(filename)) { + if (stream == null) { + fail("cannot find file: " + filename); + } + Archetype result = parser.parse(stream); + assertTrue("there should be no errors parsing " + filename + ", but was: " + parser.getErrors(), parser.getErrors().hasNoMessages()); + return result; } - Archetype result = parser.parse(stream); - assertTrue("there should be no errors parsing " + filename + ", but was: " + parser.getErrors(), parser.getErrors().hasNoMessages()); - return result; } } diff --git a/tools/src/test/java/com/nedap/archie/json/AOMJacksonTest.java b/tools/src/test/java/com/nedap/archie/json/AOMJacksonTest.java index c158810a7..5e9c4e6ae 100644 --- a/tools/src/test/java/com/nedap/archie/json/AOMJacksonTest.java +++ b/tools/src/test/java/com/nedap/archie/json/AOMJacksonTest.java @@ -6,9 +6,12 @@ import com.nedap.archie.aom.Archetype; import com.nedap.archie.aom.CComplexObject; import com.nedap.archie.aom.primitives.CDuration; +import com.nedap.archie.aom.rmoverlay.RmAttributeVisibility; +import com.nedap.archie.aom.rmoverlay.VisibilityType; import com.nedap.archie.base.Interval; import com.nedap.archie.rm.datastructures.Cluster; import com.nedap.archie.serializer.adl.ADLArchetypeSerializer; +import com.nedap.archie.testutil.TestUtil; import org.junit.Test; import org.threeten.extra.PeriodDuration; @@ -85,4 +88,34 @@ public void cDurationPeriodDuration() throws Exception { CDuration parsedDuration = objectMapper.readValue(cDurationJson, CDuration.class); assertEquals(Lists.newArrayList(new Interval<>(Duration.of(-10, ChronoUnit.HOURS), tenYearsTenSeconds)), parsedDuration.getConstraint()); } + + @Test + public void rmOverlay() throws Exception { + Archetype archetype = TestUtil.parseFailOnErrors("/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1.0.0.adls"); + String json = JacksonUtil.getObjectMapper(RMJacksonConfiguration.createStandardsCompliant()).writeValueAsString(archetype); + + Archetype parsed = JacksonUtil.getObjectMapper(RMJacksonConfiguration.createStandardsCompliant()).readValue(json, Archetype.class); + assertEquals(VisibilityType.HIDE, parsed.getRmOverlay().getRmVisibility().get("/subject").getVisibility()); + assertEquals("at12", parsed.getRmOverlay().getRmVisibility().get("/subject").getAlias().getCodeString()); + + assertTrue(json.contains(" \"rm_overlay\" : {\n" + + " \"rm_visibility\" : {\n" + + " \"/subject\" : {\n" + + " \"visibility\" : \"hide\",\n" + + " \"alias\" : {\n" + + " \"@type\" : \"TerminologyCode\",\n" + + " \"terminology_id\" : \"local\",\n" + + " \"code_string\" : \"at12\",\n" + + " \"terminology_id_string\" : \"local\"\n" + + " }\n" + + " },\n" + + " \"/data[id2]/events[id3]/data[id4]/items[id5]\" : {\n" + + " \"visibility\" : \"show\"\n" + + " },\n" + + " \"/data[id2]/events[id3]/data[id4]/items[id6]\" : {\n" + + " \"visibility\" : \"show\"\n" + + " }\n" + + " }\n" + + " },")); + } } diff --git a/tools/src/test/java/com/nedap/archie/serializer/adl/ADLArchetypeSerializerTest.java b/tools/src/test/java/com/nedap/archie/serializer/adl/ADLArchetypeSerializerTest.java index b1f2ac270..b6ade9750 100644 --- a/tools/src/test/java/com/nedap/archie/serializer/adl/ADLArchetypeSerializerTest.java +++ b/tools/src/test/java/com/nedap/archie/serializer/adl/ADLArchetypeSerializerTest.java @@ -3,12 +3,16 @@ import com.nedap.archie.adlparser.ADLParseException; import com.nedap.archie.adlparser.ADLParser; import com.nedap.archie.aom.Archetype; +import com.nedap.archie.aom.rmoverlay.VisibilityType; +import com.nedap.archie.testutil.TestUtil; import org.junit.Test; +import org.openehr.referencemodels.BuiltinReferenceModels; import java.io.IOException; import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; /** * @author markopi @@ -96,6 +100,30 @@ public void serializeAnnotations() throws Exception { assertThat(serialized, containsString("[\"local_name\"] = <\"consultation start time\">")); } + @Test + public void rmOverlay() throws Exception { + Archetype archetype = TestUtil.parseFailOnErrors("/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1.0.0.adls"); + String serialized = ADLArchetypeSerializer.serialize(archetype); + + assertTrue(serialized.contains("rm_overlay\n" + + " rm_visibility = <\n" + + " [\"/subject\"] = <\n" + + " visibility = <\"hide\">\n" + + " alias = <[local::at12]>\n" + + " >\n" + + " [\"/data[id2]/events[id3]/data[id4]/items[id5]\"] = <\n" + + " visibility = <\"show\">\n" + + " >\n" + + " [\"/data[id2]/events[id3]/data[id4]/items[id6]\"] = <\n" + + " visibility = <\"show\">\n" + + " >\n" + + " >")); + + Archetype parsed = new ADLParser(BuiltinReferenceModels.getMetaModels()).parse(serialized); + assertEquals(VisibilityType.HIDE, parsed.getRmOverlay().getRmVisibility().get("/subject").getVisibility()); + assertEquals("at12", parsed.getRmOverlay().getRmVisibility().get("/subject").getAlias().getCodeString()); + } + private Archetype load(String resourceName) throws ADLParseException, IOException { return new ADLParser().parse(ADLArchetypeSerializerTest.class.getResourceAsStream(resourceName)); } diff --git a/tools/src/test/java/com/nedap/archie/xml/JAXBAOMTest.java b/tools/src/test/java/com/nedap/archie/xml/JAXBAOMTest.java index 33ab158d7..c499306fe 100644 --- a/tools/src/test/java/com/nedap/archie/xml/JAXBAOMTest.java +++ b/tools/src/test/java/com/nedap/archie/xml/JAXBAOMTest.java @@ -8,10 +8,14 @@ import com.nedap.archie.aom.CComplexObject; import com.nedap.archie.aom.ResourceDescription; import com.nedap.archie.aom.primitives.CDuration; +import com.nedap.archie.aom.rmoverlay.VisibilityType; import com.nedap.archie.aom.terminology.ArchetypeTerminology; import com.nedap.archie.base.Interval; import com.nedap.archie.base.terminology.TerminologyCode; import com.nedap.archie.datetime.DateTimeParsers; +import com.nedap.archie.json.JacksonUtil; +import com.nedap.archie.json.RMJacksonConfiguration; +import com.nedap.archie.testutil.TestUtil; import org.junit.Before; import org.junit.Test; @@ -97,4 +101,42 @@ public void parseCDuration() throws Exception { constraint); } + @Test + public void rmOverlay() throws Exception { + Archetype archetype = TestUtil.parseFailOnErrors("/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1.0.0.adls"); + + Marshaller marshaller = JAXBUtil.getArchieJAXBContext().createMarshaller(); + StringWriter writer = new StringWriter(); + + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(archetype, writer); + + assertTrue(writer.toString().contains("\n" + + " \n" + + " /subject\n" + + " hide\n" + + " \n" + + " local\n" + + " at12\n" + + " \n" + + " \n" + + " \n" + + " /data[id2]/events[id3]/data[id4]/items[id5]\n" + + " show\n" + + " \n" + + " \n" + + " /data[id2]/events[id3]/data[id4]/items[id6]\n" + + " show\n" + + " \n" + + " ")); + + Unmarshaller unmarshaller = JAXBUtil.getArchieJAXBContext().createUnmarshaller(); + Archetype unmarshalled = (Archetype) unmarshaller.unmarshal(new StringReader(writer.toString())); + + assertEquals(VisibilityType.HIDE, unmarshalled.getRmOverlay().getRmVisibility().get("/subject").getVisibility()); + assertEquals("at12", unmarshalled.getRmOverlay().getRmVisibility().get("/subject").getAlias().getCodeString()); + + } + + } diff --git a/tools/src/test/resources/adl2-tests/validity/rm_overlay/openEHR-EHR-OBSERVATION.overlay_invalid_path.v1.0.0.adls b/tools/src/test/resources/adl2-tests/validity/rm_overlay/openEHR-EHR-OBSERVATION.overlay_invalid_path.v1.0.0.adls new file mode 100644 index 000000000..c0e816e7d --- /dev/null +++ b/tools/src/test/resources/adl2-tests/validity/rm_overlay/openEHR-EHR-OBSERVATION.overlay_invalid_path.v1.0.0.adls @@ -0,0 +1,107 @@ +archetype (adl_version=2.0.5; rm_release=1.0.2; generated) + openEHR-EHR-OBSERVATION.overlay_invalid_path.v1.0.0 + +language + original_language = <[ISO_639-1::en]> + +description + original_author = < + ["name"] = <"Pieter Bos"> + > + details = < + ["en"] = < + language = <[ISO_639-1::en]> + purpose = <"Test for flattening"> + keywords = <"ADL", "test"> + > + > + lifecycle_state = <"published"> + other_details = < + ["regression"] = <"VRANP"> + > + copyright = <"copyright © 2004 openEHR Foundation "> + +definition + OBSERVATION[id1] matches { -- Blood pressure + data matches { + HISTORY[id2] matches { + events cardinality matches {1..*; unordered} matches { + EVENT[id3] occurrences matches {1..*} matches { -- Any event + data matches { + ITEM_TREE[id4] matches { + items cardinality matches {3; unordered} matches { + ELEMENT[id5] matches { -- systolic pressure + value matches { + DV_QUANTITY[id13] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + ELEMENT[id6] matches { -- diastolic pressure + value matches { + DV_QUANTITY[id14] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + ELEMENT[id7] matches { -- difference between systolic and diastolic. For testing purposes :) + value matches { + DV_QUANTITY[id15] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + } + } + } + } + } + } + } + } + +rm_overlay + rm_visibility = < + ["/subject_not"] = < + visibility = <"hide"> + alias = <[local::at12]> + > + ["/data[id2]/events[id71]/data[id4]/items[id5]"] = < + visibility = <"show"> + > + ["/data[id2]/events[id3]/data[id4]/items[id6]"] = < + visibility = <"show"> + > + > + +terminology + term_definitions = < + ["en"] = < + ["id1"] = < + text = <"Blood Pressure"> + description = <"The local measurement of arterial blood pressure which is a surrogate for arterial. pressure in the systemic circulation. Most commonly, use of the term 'blood pressure' refers to measurement of brachial artery pressure in the upper arm."> + > + ["id3"] = < + text = <"any event"> + description = <"any event"> + > + ["id5"] = < + text = <"systolic"> + description = <"Systolic Pressure"> + > + ["id6"] = < + text = <"diastolic pressure"> + description = <"-- diastolic pressure"> + > + ["id7"] = < + text = <"pressure difference"> + description = <"testing 1 2 3 "> + > + > + > diff --git a/tools/src/test/resources/adl2-tests/validity/rm_overlay/openEHR-EHR-OBSERVATION.overlay_missing_ad_code.v1.0.0.adls b/tools/src/test/resources/adl2-tests/validity/rm_overlay/openEHR-EHR-OBSERVATION.overlay_missing_ad_code.v1.0.0.adls new file mode 100644 index 000000000..967f15553 --- /dev/null +++ b/tools/src/test/resources/adl2-tests/validity/rm_overlay/openEHR-EHR-OBSERVATION.overlay_missing_ad_code.v1.0.0.adls @@ -0,0 +1,107 @@ +archetype (adl_version=2.0.5; rm_release=1.0.2; generated) + openEHR-EHR-OBSERVATION.overlay_missing_ad_code.v1.0.0 + +language + original_language = <[ISO_639-1::en]> + +description + original_author = < + ["name"] = <"Pieter Bos"> + > + details = < + ["en"] = < + language = <[ISO_639-1::en]> + purpose = <"Test for flattening"> + keywords = <"ADL", "test"> + > + > + lifecycle_state = <"published"> + other_details = < + ["regression"] = <"VATID"> + > + copyright = <"copyright © 2004 openEHR Foundation "> + +definition + OBSERVATION[id1] matches { -- Blood pressure + data matches { + HISTORY[id2] matches { + events cardinality matches {1..*; unordered} matches { + EVENT[id3] occurrences matches {1..*} matches { -- Any event + data matches { + ITEM_TREE[id4] matches { + items cardinality matches {3; unordered} matches { + ELEMENT[id5] matches { -- systolic pressure + value matches { + DV_QUANTITY[id13] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + ELEMENT[id6] matches { -- diastolic pressure + value matches { + DV_QUANTITY[id14] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + ELEMENT[id7] matches { -- difference between systolic and diastolic. For testing purposes :) + value matches { + DV_QUANTITY[id15] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + } + } + } + } + } + } + } + } + +rm_overlay + rm_visibility = < + ["/subject"] = < + visibility = <"hide"> + alias = <[local::at12]> + > + ["/data[id2]/events[id3]/data[id4]/items[id5]"] = < + visibility = <"show"> + > + ["/data[id2]/events[id3]/data[id4]/items[id6]"] = < + visibility = <"show"> + > + > + +terminology + term_definitions = < + ["en"] = < + ["id1"] = < + text = <"Blood Pressure"> + description = <"The local measurement of arterial blood pressure which is a surrogate for arterial. pressure in the systemic circulation. Most commonly, use of the term 'blood pressure' refers to measurement of brachial artery pressure in the upper arm."> + > + ["id3"] = < + text = <"any event"> + description = <"any event"> + > + ["id5"] = < + text = <"systolic"> + description = <"Systolic Pressure"> + > + ["id6"] = < + text = <"diastolic pressure"> + description = <"-- diastolic pressure"> + > + ["id7"] = < + text = <"pressure difference"> + description = <"testing 1 2 3 "> + > + > + > diff --git a/tools/src/test/resources/adl2-tests/validity/rm_overlay/openEHR-EHR-OBSERVATION.valid_overlay.v1.0.0.adls b/tools/src/test/resources/adl2-tests/validity/rm_overlay/openEHR-EHR-OBSERVATION.valid_overlay.v1.0.0.adls new file mode 100644 index 000000000..82fd2d2b3 --- /dev/null +++ b/tools/src/test/resources/adl2-tests/validity/rm_overlay/openEHR-EHR-OBSERVATION.valid_overlay.v1.0.0.adls @@ -0,0 +1,111 @@ +archetype (adl_version=2.0.5; rm_release=1.0.2; generated) + openEHR-EHR-OBSERVATION.valid_overlay.v1.0.0 + +language + original_language = <[ISO_639-1::en]> + +description + original_author = < + ["name"] = <"Pieter Bos"> + > + details = < + ["en"] = < + language = <[ISO_639-1::en]> + purpose = <"Test for flattening"> + keywords = <"ADL", "test"> + > + > + lifecycle_state = <"published"> + other_details = < + ["regression"] = <"PASS"> + > + copyright = <"copyright © 2004 openEHR Foundation "> + +definition + OBSERVATION[id1] matches { -- Blood pressure + data matches { + HISTORY[id2] matches { + events cardinality matches {1..*; unordered} matches { + EVENT[id3] occurrences matches {1..*} matches { -- Any event + data matches { + ITEM_TREE[id4] matches { + items cardinality matches {3; unordered} matches { + ELEMENT[id5] matches { -- systolic pressure + value matches { + DV_QUANTITY[id13] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + ELEMENT[id6] matches { -- diastolic pressure + value matches { + DV_QUANTITY[id14] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + ELEMENT[id7] matches { -- difference between systolic and diastolic. For testing purposes :) + value matches { + DV_QUANTITY[id15] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + } + } + } + } + } + } + } + } + +rm_overlay + rm_visibility = < + ["/subject"] = < + visibility = <"hide"> + alias = <[local::at12]> + > + ["/data[id2]/events[id3]/data[id4]/items[id5]"] = < + visibility = <"show"> + > + ["/data[id2]/events[id3]/data[id4]/items[id6]/null_flavour"] = < + visibility = <"show"> + > + > + +terminology + term_definitions = < + ["en"] = < + ["id1"] = < + text = <"Blood Pressure"> + description = <"The local measurement of arterial blood pressure which is a surrogate for arterial. pressure in the systemic circulation. Most commonly, use of the term 'blood pressure' refers to measurement of brachial artery pressure in the upper arm."> + > + ["id3"] = < + text = <"any event"> + description = <"any event"> + > + ["id5"] = < + text = <"systolic"> + description = <"Systolic Pressure"> + > + ["id6"] = < + text = <"diastolic pressure"> + description = <"-- diastolic pressure"> + > + ["id7"] = < + text = <"pressure difference"> + description = <"testing 1 2 3 "> + > + ["at12"] = < + text = <"some subject"> + description = <"ingocnito mode activated"> + > + > + > diff --git a/tools/src/test/resources/com/nedap/archie/flattener/openEHR-COMPOSITION.including_overlay.v1.0.0.adls b/tools/src/test/resources/com/nedap/archie/flattener/openEHR-COMPOSITION.including_overlay.v1.0.0.adls new file mode 100644 index 000000000..fba72c056 --- /dev/null +++ b/tools/src/test/resources/com/nedap/archie/flattener/openEHR-COMPOSITION.including_overlay.v1.0.0.adls @@ -0,0 +1,42 @@ +archetype (adl_version=2.0.5; rm_release=1.0.2; generated) + openEHR-EHR-COMPOSITION.including_overlay.v1.0.0 + +language + original_language = <[ISO_639-1::en]> + +description + lifecycle_state = <"unmanaged"> + original_author = < + ["name"] = <"Pieter Bos"> + > + + details = < + ["en"] = < + language = <[ISO_639-1::en]> + purpose = <"test."> + use = <"testing"> + keywords = <"test", "rm_overlay"> + misuse = <""> + > + > + +definition + COMPOSITION[id1] matches { -- Report + content matches { + use_archetype OBSERVATION[id2, openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1] + } + } + +terminology + term_definitions = < + ["en"] = < + ["id1"] = < + text = <"Report"> + description = <"Document to communicate information to others, commonly in response to a request from another party."> + > + ["id2"] = < + text = <"included observation with overlay"> + description = <"included observation with overlay."> + > + > + > \ No newline at end of file diff --git a/tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_child_with_overlay.v1.0.0.adls b/tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_child_with_overlay.v1.0.0.adls new file mode 100644 index 000000000..ea5104bde --- /dev/null +++ b/tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_child_with_overlay.v1.0.0.adls @@ -0,0 +1,44 @@ +archetype (adl_version=2.0.5; rm_release=1.0.2; generated) + openEHR-EHR-OBSERVATION.to_flatten_child_with_overlay.v1.0.0 + +specialize + openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1 + +language + original_language = <[ISO_639-1::en]> + +description + lifecycle_state = <"unmanaged"> + original_author = < + ["name"] = <"Pieter Bos"> + > + copyright = <"none"> + details = < + ["en"] = < + language = <[ISO_639-1::en]> + purpose = <"specialized rules test"> + use = <"testing specialized rules"> + keywords = <"test"> + misuse = <""> + > + > + +definition + OBSERVATION[id1.1] -- blood pressure + +rm_overlay + rm_visibility = < + ["/state"] = < + visibility = <"hide"> + > + > + +terminology + term_definitions = < + ["en"] = < + ["id1.1"] = < + text = <"blood pressure observation"> + description = <"blood pressure observation"> + > + > + > \ No newline at end of file diff --git a/tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1.0.0.adls b/tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1.0.0.adls new file mode 100644 index 000000000..ac12aaef8 --- /dev/null +++ b/tools/src/test/resources/com/nedap/archie/flattener/openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1.0.0.adls @@ -0,0 +1,111 @@ +archetype (adl_version=2.0.5; rm_release=1.0.2; generated) + openEHR-EHR-OBSERVATION.to_flatten_parent_with_overlay.v1.0.0 + +language + original_language = <[ISO_639-1::en]> + +description + original_author = < + ["name"] = <"Pieter Bos"> + > + details = < + ["en"] = < + language = <[ISO_639-1::en]> + purpose = <"Test for flattening"> + keywords = <"ADL", "test"> + > + > + lifecycle_state = <"published"> + other_details = < + ["regression"] = <"PASS"> + > + copyright = <"copyright © 2004 openEHR Foundation "> + +definition + OBSERVATION[id1] matches { -- Blood pressure + data matches { + HISTORY[id2] matches { + events cardinality matches {1..*; unordered} matches { + EVENT[id3] occurrences matches {1..*} matches { -- Any event + data matches { + ITEM_TREE[id4] matches { + items cardinality matches {3; unordered} matches { + ELEMENT[id5] matches { -- systolic pressure + value matches { + DV_QUANTITY[id13] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + ELEMENT[id6] matches { -- diastolic pressure + value matches { + DV_QUANTITY[id14] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + ELEMENT[id7] matches { -- difference between systolic and diastolic. For testing purposes :) + value matches { + DV_QUANTITY[id15] matches { + [magnitude, units, precision] matches { + [{|0.0..<1000.0|}, {"mmHg"}, {1}] + } + } + } + } + } + } + } + } + } + } + } + } + +rm_overlay + rm_visibility = < + ["/subject"] = < + visibility = <"hide"> + alias = <[local::at12]> + > + ["/data[id2]/events[id3]/data[id4]/items[id5]"] = < + visibility = <"show"> + > + ["/data[id2]/events[id3]/data[id4]/items[id6]"] = < + visibility = <"show"> + > + > + +terminology + term_definitions = < + ["en"] = < + ["id1"] = < + text = <"Blood Pressure"> + description = <"The local measurement of arterial blood pressure which is a surrogate for arterial. pressure in the systemic circulation. Most commonly, use of the term 'blood pressure' refers to measurement of brachial artery pressure in the upper arm."> + > + ["id3"] = < + text = <"any event"> + description = <"any event"> + > + ["id5"] = < + text = <"systolic"> + description = <"Systolic Pressure"> + > + ["id6"] = < + text = <"diastolic pressure"> + description = <"-- diastolic pressure"> + > + ["id7"] = < + text = <"pressure difference"> + description = <"testing 1 2 3 "> + > + ["at12"] = < + text = <"some subject"> + description = <"ingocnito mode activated"> + > + > + > diff --git a/utils/src/main/java/com/nedap/archie/paths/PathUtil.java b/utils/src/main/java/com/nedap/archie/paths/PathUtil.java index 5757cf891..bd9cdc4a8 100644 --- a/utils/src/main/java/com/nedap/archie/paths/PathUtil.java +++ b/utils/src/main/java/com/nedap/archie/paths/PathUtil.java @@ -27,6 +27,10 @@ public static String getPath(List pathSegments) { result.append(segment.getIndex().toString()); } result.append("]"); + } else if (segment.hasNumberIndex()) { + result.append("["); + result.append(segment.getIndex()); + result.append("]"); } } return result.toString();