diff --git a/aom/src/main/java/com/nedap/archie/adl14/ADL14DefaultMultiplicitiesSetter.java b/aom/src/main/java/com/nedap/archie/adl14/ADL14DefaultMultiplicitiesSetter.java index 4def296fb..8f158a48a 100644 --- a/aom/src/main/java/com/nedap/archie/adl14/ADL14DefaultMultiplicitiesSetter.java +++ b/aom/src/main/java/com/nedap/archie/adl14/ADL14DefaultMultiplicitiesSetter.java @@ -9,10 +9,10 @@ import com.nedap.archie.rminfo.MetaModels; /** - * Sets the default occurrences with ADL 1.4 rules, if not explicitly set in a given Archetype. - * Useful for conversion to ADL 2, where the default values are different, and it is good to start + * Sets the default occurrences with ADL 1.4 rules ({1..1}), if not explicitly set in a given Archetype. + * Useful for conversion to ADL 2, where the default values are different ({0..*}), and it is good to start * with the correct values already present. - * + *

* Cardinality and existence are also specified to have a default value. However, this is not used * in practice (source, several openEHR community members). Adding that to the conversion would * lead to problems. So these are left out. @@ -31,20 +31,9 @@ public void setDefaults(Archetype archetype) { } private void correctItemsMultiplicities(CObject cObject) { - for(CAttribute attribute:cObject.getAttributes()) { - // according to the specification, the following lines must be added. - // however, in practice this is not followed, and adding it would - // lead to more problems - /* - if(attribute.getCardinality() == null) { - attribute.setCardinality(new Cardinality(0, 1)); - } - if(attribute.getExistence() == null) { - attribute.setExistence(new MultiplicityInterval(1, 1)); - }*/ - for(CObject child:attribute.getChildren()) { - if(child.getOccurrences() == null && - metaModels.isMultiple(cObject.getRmTypeName(), attribute.getRmAttributeName())) { + for (CAttribute attribute : cObject.getAttributes()) { + for (CObject child : attribute.getChildren()) { + if (child.getOccurrences() == null && metaModels.isMultiple(cObject.getRmTypeName(), attribute.getRmAttributeName())) { child.setOccurrences(new MultiplicityInterval(1, 1)); } correctItemsMultiplicities(child); diff --git a/aom/src/main/java/com/nedap/archie/adl14/ADL14DescriptionConverter.java b/aom/src/main/java/com/nedap/archie/adl14/ADL14DescriptionConverter.java index 11d105214..d3da0474f 100644 --- a/aom/src/main/java/com/nedap/archie/adl14/ADL14DescriptionConverter.java +++ b/aom/src/main/java/com/nedap/archie/adl14/ADL14DescriptionConverter.java @@ -6,6 +6,11 @@ import java.util.LinkedHashMap; import java.util.Map; +/** + * Converts the Description section from ADL1.4 to ADL2 + * Specifications ADL1.4 + * Specifications ADL2 + */ public class ADL14DescriptionConverter { public void convert(Archetype archetype) { @@ -44,12 +49,10 @@ public void convert(Archetype archetype) { } description.setIpAcknowledgements(acknowledgements); } - String revision = description.getOtherDetails().remove("revision"); if(revision != null) { archetype.getArchetypeId().setReleaseVersion(revision); } - archetype.setGenerated(true); } } diff --git a/aom/src/main/java/com/nedap/archie/adl14/ADL14NodeIDConverter.java b/aom/src/main/java/com/nedap/archie/adl14/ADL14NodeIDConverter.java index 6a3120117..48ad8663d 100644 --- a/aom/src/main/java/com/nedap/archie/adl14/ADL14NodeIDConverter.java +++ b/aom/src/main/java/com/nedap/archie/adl14/ADL14NodeIDConverter.java @@ -4,14 +4,7 @@ import com.nedap.archie.adl14.log.ConvertedCodeResult; import com.nedap.archie.adl14.log.CreatedCode; import com.nedap.archie.adl14.log.ReasonForCodeCreation; -import com.nedap.archie.aom.Archetype; -import com.nedap.archie.aom.ArchetypeSlot; -import com.nedap.archie.aom.CArchetypeRoot; -import com.nedap.archie.aom.CAttribute; -import com.nedap.archie.aom.CComplexObject; -import com.nedap.archie.aom.CComplexObjectProxy; -import com.nedap.archie.aom.CObject; -import com.nedap.archie.aom.CPrimitiveObject; +import com.nedap.archie.aom.*; import com.nedap.archie.aom.primitives.CString; import com.nedap.archie.aom.terminology.ArchetypeTerm; import com.nedap.archie.aom.terminology.ValueSet; @@ -21,19 +14,10 @@ import com.nedap.archie.paths.PathSegment; import com.nedap.archie.query.APathQuery; import com.nedap.archie.rminfo.MetaModels; -import com.nedap.archie.rules.Assertion; -import com.nedap.archie.rules.BinaryOperator; -import com.nedap.archie.rules.Constraint; -import com.nedap.archie.rules.Expression; -import com.nedap.archie.rules.OperatorKind; +import com.nedap.archie.rules.*; import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -56,12 +40,10 @@ public class ADL14NodeIDConverter { /** * Contains a mapping between the adl 1.4 codes and the generated adl 2 code */ - private Map convertedCodes = new LinkedHashMap<>(); - private Map createdCodes = new LinkedHashMap<>(); - private Map createdValueSets = new LinkedHashMap<>(); - - private Map newCodeToOldCodeMap = new LinkedHashMap<>(); - + private final Map convertedCodes = new LinkedHashMap<>(); + private final Map createdCodes = new LinkedHashMap<>(); + private final Map createdValueSets = new LinkedHashMap<>(); + private final Map newCodeToOldCodeMap = new LinkedHashMap<>(); public ADL14NodeIDConverter(MetaModels metaModels, Archetype archetype, Archetype flatParentArchetype, ADL14ConversionConfiguration configuration, ADL2ConversionLog oldLog, ADL2ConversionResult conversionResult) { this.metaModels = metaModels; @@ -74,51 +56,52 @@ public ADL14NodeIDConverter(MetaModels metaModels, Archetype archetype, Archetyp } - public ADL14ConversionConfiguration getConversionConfiguration() { return conversionConfiguration; } public ADL2ConversionLog convert() { metaModels.selectModel(archetype); + correctItemsCardinality(archetype.getDefinition()); List unnecessaryCodes = findUnnecessaryCodes(archetype.getDefinition(), archetype.getTerminology().getTermDefinitions().get(archetype.getOriginalLanguage().getCodeString())); convert(archetype.getDefinition()); - if(previousConversionApplier != null) { - //tricky stuff here: - //apply the previous conversion. This does 3 things: - //1. add synthesized id codes in the same was as before - //2. create previously synthesized term codes plus binding again - //3. create previously created value sets again - //These processes are not tricky in itself, but in what effect it has on the rest of the conversion process - //for example, if you add an at-code to the term bindings here, it must not be converted again - //this is done by keeping a log of converted codes in this class, and not converting any term bindings - //unless they are in the converted codes. - previousConversionApplier.addCreatedCodes(); - previousConversionApplier.addValueSets(); - } + + //tricky stuff here: + //apply the previous conversion. This does 3 things: + //1. add synthesized id codes in the same was as before + //2. create previously synthesized term codes plus binding again + //3. create previously created value sets again + //These processes are not tricky in itself, but in what effect it has on the rest of the conversion process + //for example, if you add an at-code to the term bindings here, it must not be converted again + //this is done by keeping a log of converted codes in this class, and not converting any term bindings + //unless they are in the converted codes. + previousConversionApplier.addCreatedCodes(); + previousConversionApplier.addValueSets(); this.idCodeGenerator = new OutsideRangeIdCodeGenerator(archetype); termConstraintConverter.convert(); + convertTermBindings(archetype); generateMissingNodeIds(archetype.getDefinition()); - convertTermDefinitions(archetype, convertedCodes, unnecessaryCodes); previousConversionApplier.removeCreatedUnusedTermCodesAndValueSets(); return new ADL2ConversionLog(/*convertedCodes*/ null, createdCodes, createdValueSets); } + /** + * Correct invalid CLUSTER.items cardinality, since for a cluster cardinality must be >= 1 + */ private void correctItemsCardinality(CObject cObject) { - for(CAttribute attribute:cObject.getAttributes()) { - if(attribute.getRmAttributeName().equalsIgnoreCase("items") && cObject.getRmTypeName().equalsIgnoreCase("CLUSTER") && attribute.getCardinality() != null) { + for (CAttribute attribute : cObject.getAttributes()) { + if (attribute.getRmAttributeName().equalsIgnoreCase("items") && cObject.getRmTypeName().equalsIgnoreCase("CLUSTER") && attribute.getCardinality() != null) { Cardinality cardinality = attribute.getCardinality(); - if(cardinality.getInterval().isUpperUnbounded() && cardinality.getInterval().getLower() == 0 && cardinality.getInterval().isLowerIncluded()) { - //ok this is an invalid CLUSTER.items cardinality, since for a cluster this is apparently >= 1. + if (cardinality.getInterval().isUpperUnbounded() && cardinality.getInterval().getLower() == 0 && cardinality.getInterval().isLowerIncluded()) { cardinality.getInterval().setLower(1); } } - for(CObject child:attribute.getChildren()) { + for (CObject child : attribute.getChildren()) { correctItemsCardinality(child); } } @@ -127,10 +110,10 @@ private void correctItemsCardinality(CObject cObject) { /** * Find which node identifiers don't need to be in the archetype terminology in ADL2. - * + *

* The identifiers of single object nodes defined under single-valued attributes don't need terminology definitions, * so filter those which have a short text and "@ internal @" in the description in the original language. - * + * * @see ADL2 - 4.3.4.1. Node Identifiers */ private List findUnnecessaryCodes(CObject cObject, Map originalLanguageTermDefinitions) { @@ -153,15 +136,18 @@ private List findUnnecessaryCodes(CObject cObject, Map convertedCodes, List unnecessaryCodes) { //process the codes in alphabetical order, high to low, to prevent overwriting codes //even better would probably be to create an empty terminology and separate all new+converted codes and old codes //instead of doing this in place. Worth a refactor perhaps? ArrayList sortedCodes = new ArrayList<>(convertedCodes.values()); - Comparator comparator = Comparator.comparing(r -> r.getOriginalCode()); + Comparator comparator = Comparator.comparing(ConvertedCodeResult::getOriginalCode); sortedCodes.sort(comparator.reversed()); - for(ConvertedCodeResult convertedCode: sortedCodes) { + for (ConvertedCodeResult convertedCode : sortedCodes) { for (String language : archetype.getTerminology().getTermDefinitions().keySet()) { Map terms = archetype.getTerminology().getTermDefinitions().get(language); ArchetypeTerm term = terms.remove(convertedCode.getOriginalCode()); @@ -181,20 +167,24 @@ public static void convertTermDefinitions(Archetype archetype, Map> newTermBindings = new LinkedHashMap<>(); - for(String terminologyid: archetype.getTerminology().getTermBindings().keySet()) { - Map termbindingMap = archetype.getTerminology().getTermBindings().get(terminologyid); + for (String terminologyId : archetype.getTerminology().getTermBindings().keySet()) { + Map termbindingMap = archetype.getTerminology().getTermBindings().get(terminologyId); Map newTermbindingsMap = new LinkedHashMap<>(); - newTermBindings.put(terminologyid, newTermbindingsMap); - for(String valueOrPath:termbindingMap.keySet()) { - if(valueOrPath.startsWith("/")) { + newTermBindings.put(terminologyId, newTermbindingsMap); + for (String valueOrPath : termbindingMap.keySet()) { + if (valueOrPath.startsWith("/")) { String newPath = convertPath(valueOrPath); newTermbindingsMap.put(newPath, termbindingMap.get(valueOrPath)); } else if (AOMUtils.isValueCode(valueOrPath)) { - if(convertedCodes.containsKey(valueOrPath)) { - for(String newCode:convertedCodes.get(valueOrPath).getConvertedCodes()) { + if (convertedCodes.containsKey(valueOrPath)) { + for (String newCode : convertedCodes.get(valueOrPath).getConvertedCodes()) { newTermbindingsMap.put(newCode, termbindingMap.get(valueOrPath)); } } else { @@ -203,17 +193,17 @@ private void convertTermBindings(Archetype archetype) { conversionResult.getLog().addWarningWithLocation(ADL14ConversionMessageCode.WARNING_UNKNOWN_CODE_TYPE_IN_TERMBINDING, valueOrPath, valueOrPath); } } else if (AOMUtils.isValueSetCode(valueOrPath)) { - if(convertedCodes.containsKey(valueOrPath)) { - for(String newCode:convertedCodes.get(valueOrPath).getConvertedCodes()) { + if (convertedCodes.containsKey(valueOrPath)) { + for (String newCode : convertedCodes.get(valueOrPath).getConvertedCodes()) { newTermbindingsMap.put(newCode, termbindingMap.get(valueOrPath)); } } else { //unused value set code, this can be converted - newTermbindingsMap.put(this.convertCode(valueOrPath, "ac"), termbindingMap.get(valueOrPath)); + newTermbindingsMap.put(convertCode(valueOrPath, "ac"), termbindingMap.get(valueOrPath)); } } else if (AOMUtils.isIdCode(valueOrPath)) { - if(convertedCodes.containsKey(valueOrPath)) { - for(String newCode:convertedCodes.get(valueOrPath).getConvertedCodes()) { + if (convertedCodes.containsKey(valueOrPath)) { + for (String newCode : convertedCodes.get(valueOrPath).getConvertedCodes()) { newTermbindingsMap.put(newCode, termbindingMap.get(valueOrPath)); } } else { @@ -231,28 +221,31 @@ private void convertTermBindings(Archetype archetype) { } } + /** + * Add missing node id's for CObjects, as long as they are not CPrimitiveObjects + */ private void generateMissingNodeIds(CObject cObject) { - if(!(cObject instanceof CPrimitiveObject) && cObject.getNodeId() == null) { + if (!(cObject instanceof CPrimitiveObject) && cObject.getNodeId() == null) { String path = cObject.getPath(); - if(archetype.getParentArchetypeId() != null && flatParentArchetype != null) { + if (archetype.getParentArchetypeId() != null && flatParentArchetype != null) { //get the matching path in the parent archetype id. Find this node //if found, this is a specialization of said node and needs to be checked for differences and/or //given the same node id //if not found, generate/synthesize a new node id. - String parentPath = AOMUtils.pathAtSpecializationLevel(cObject.getPathSegments(), archetype.specializationDepth()-1); + String parentPath = AOMUtils.pathAtSpecializationLevel(cObject.getPathSegments(), archetype.specializationDepth() - 1); CAttribute cAttributeInParent = flatParentArchetype.itemAtPath(parentPath); - if(cAttributeInParent != null) { - List childrenWithSameRmTypeName = cAttributeInParent.getChildrenByRmTypeName (cObject.getRmTypeName()); - if(childrenWithSameRmTypeName.size() == 1 ) { + if (cAttributeInParent != null) { + List childrenWithSameRmTypeName = cAttributeInParent.getChildrenByRmTypeName(cObject.getRmTypeName()); + if (childrenWithSameRmTypeName.size() == 1) { cObject.setNodeId(childrenWithSameRmTypeName.get(0).getNodeId()); - } else if(childrenWithSameRmTypeName.size() > 1 ) { + } else if (childrenWithSameRmTypeName.size() > 1) { //this is a bit odd - it now specializes the first child it finds. Let's warn synthesizeNodeId(cObject, path); conversionResult.getLog().addWarningWithLocation(ADL14ConversionMessageCode.WARNING_SPECIALIZED_FIRST_MATCHING_CHILD, cObject.path()); } else if (cAttributeInParent.getChildren().size() == 1 || cObject.getParent().getChildren().size() == 1) { - if(this.metaModels.rmTypesConformant(cObject.getRmTypeName(), cAttributeInParent.getChildren().get(0).getRmTypeName())) { + if (this.metaModels.rmTypesConformant(cObject.getRmTypeName(), cAttributeInParent.getChildren().get(0).getRmTypeName())) { //this replaces a parent node, so a specialisation. add id code and possibly a term createSpecialisedNodeId(cObject, path, Arrays.asList(cAttributeInParent.getChildren().get(0))); } else { @@ -269,17 +262,21 @@ private void generateMissingNodeIds(CObject cObject) { } else { synthesizeNodeId(cObject, path); } - } else { synthesizeNodeId(cObject, path); } } - for(CAttribute attribute:cObject.getAttributes()) { - generateMissingNodeIds(attribute); + for (CAttribute attribute : cObject.getAttributes()) { + for (CObject object : attribute.getChildren()) { + generateMissingNodeIds(object); + } } } + /** + * Object is a specialised node, create a specialised nodeId and add it to the terminology + */ private void createSpecialisedNodeId(CObject cObject, String path, List childrenWithSameRmTypeName) { cObject.setNodeId(idCodeGenerator.generateNextSpecializedIdCode(childrenWithSameRmTypeName.get(0).getNodeId())); CreatedCode createdCode = new CreatedCode(cObject.getNodeId(), ReasonForCodeCreation.C_OBJECT_WITHOUT_NODE_ID); @@ -289,6 +286,9 @@ private void createSpecialisedNodeId(CObject cObject, String path, List createTermForNewCode(cObject); } + /** + * Object needs a new nodeId, generate the next valid nodeId and add in to the terminology + */ private void synthesizeNodeId(CObject cObject, String path) { cObject.setNodeId(idCodeGenerator.generateNextIdCode()); CreatedCode createdCode = new CreatedCode(cObject.getNodeId(), ReasonForCodeCreation.C_OBJECT_WITHOUT_NODE_ID); @@ -298,12 +298,15 @@ private void synthesizeNodeId(CObject cObject, String path) { createTermForNewCode(cObject); } + /** + * Create an instance of the new nodeId to the terminology + */ private void createTermForNewCode(CObject cObject) { - if(cObject.getParent().isMultiple()) { - for(String language: archetype.getTerminology().getTermDefinitions().keySet()) { + if (cObject.getParent().isMultiple()) { + for (String language : archetype.getTerminology().getTermDefinitions().keySet()) { //TODO: add new archetype term to conversion log? ArchetypeTerm term = termConstraintConverter.getTerm(language, cObject); - if(term != null) { + if (term != null) { ArchetypeTerm newTerm = new ArchetypeTerm(); newTerm.setCode(cObject.getNodeId()); newTerm.setText(term.getText() + " (synthesised)"); @@ -322,42 +325,34 @@ protected void addCreatedValueSet(String nodeId, ValueSet valueSet) { this.createdValueSets.put(nodeId, valueSet); } - private void generateMissingNodeIds(CAttribute attribute) { - for(CObject cObject:attribute.getChildren()) { - generateMissingNodeIds(cObject); - } - } - + /** + * Convert node id's for cObject and all child objects within + */ private void convert(CObject cObject) { - - if(cObject instanceof CComplexObject) { - if (cObject instanceof CArchetypeRoot) { - - } + if (cObject instanceof CComplexObject) { calculateNewNodeId(cObject); } else if (cObject instanceof ArchetypeSlot) { - calculateNewNodeId(cObject); if (cObject.getNodeId() != null) { //VSSID validation does not exist in ADL 1.4. Fix it here - if(flatParentArchetype != null) { + if (flatParentArchetype != null) { String parentPath = AOMUtils.pathAtSpecializationLevel(cObject.getPathSegments(), archetype.specializationDepth() - 1); CObject cObjectInParent = flatParentArchetype.itemAtPath(parentPath); - if (cObjectInParent != null && cObjectInParent instanceof ArchetypeSlot && !cObjectInParent.getNodeId().equalsIgnoreCase(cObject.getNodeId())) { + if (cObjectInParent instanceof ArchetypeSlot && !cObjectInParent.getNodeId().equalsIgnoreCase(cObject.getNodeId())) { //specializing a node id for an archetype slot is not allowed in ADL 2. Set to parent node id. cObject.setNodeId(cObjectInParent.getNodeId()); //TODO: remove id code from terminology as well } } ArchetypeSlot slot = (ArchetypeSlot) cObject; - for(Assertion assertion:slot.getIncludes()) { - if(assertion.getExpression() != null) { + for (Assertion assertion : slot.getIncludes()) { + if (assertion.getExpression() != null) { fixArchetypeSlotExpression(assertion.getExpression()); } } - for(Assertion assertion:slot.getExcludes()) { - if(assertion.getExpression() != null) { + for (Assertion assertion : slot.getExcludes()) { + if (assertion.getExpression() != null) { fixArchetypeSlotExpression(assertion.getExpression()); } } @@ -367,14 +362,20 @@ private void convert(CObject cObject) { calculateNewNodeId(cObject); proxy.setTargetPath(convertPath(proxy.getTargetPath())); } - for(CAttribute attribute:cObject.getAttributes()) { - convert(attribute); + for (CAttribute attribute : cObject.getAttributes()) { + for (CObject object : attribute.getChildren()) { + convert(object); + } } } + /** + * Fix archetype slot regex for version of archetype + * ADL1.4: \.v1 + * ADL2: \.v1\..* + */ private static final Pattern ARCHETYPE_ID_ENDS_WITH_VERSION_PATTERN = Pattern.compile(".*v[0-9]+"); private static final Pattern ARCHETYPE_ID_ENDS_WITH_VERSION_PATTERN_REPLACE = Pattern.compile("(?\\.v[0-9]+)(?([\\|]|\\z))");//\z means end of input - private static void fixArchetypeSlotExpression(Expression expression) { //match patterns in the form archetype_id/value matches {/regexp/} or archetype_id/value matches {"string"} if (expression instanceof BinaryOperator) { @@ -383,10 +384,10 @@ private static void fixArchetypeSlotExpression(Expression expression) { Expression rightOperand = binary.getRightOperand(); if (rightOperand instanceof Constraint) { Constraint constraint = (Constraint) rightOperand; - if(constraint.getItem() != null && constraint.getItem().getConstraint() != null && constraint.getItem().getConstraint().size() > 0 && + if (constraint.getItem() != null && constraint.getItem().getConstraint() != null && !constraint.getItem().getConstraint().isEmpty() && constraint.getItem() instanceof CString) { CString cString = (CString) constraint.getItem(); - if(cString.getConstraint() == null || cString.getConstraint().isEmpty()) { + if (cString.getConstraint() == null || cString.getConstraint().isEmpty()) { return; } String pattern = cString.getConstraint().get(0); @@ -394,14 +395,14 @@ private static void fixArchetypeSlotExpression(Expression expression) { //regexp pattern = pattern.substring(1, pattern.length() - 1); Matcher matcher = ARCHETYPE_ID_ENDS_WITH_VERSION_PATTERN_REPLACE.matcher(pattern); - if(matcher.find()) { + if (matcher.find()) { pattern = "/" + matcher.replaceAll("${version}\\\\..*${end}") + "/"; cString.getConstraint().remove(0); cString.getConstraint().add(pattern); } } else { //string instead of regexp. does this happen in any archetypes? - if(ARCHETYPE_ID_ENDS_WITH_VERSION_PATTERN.matcher(pattern).matches()) { + if (ARCHETYPE_ID_ENDS_WITH_VERSION_PATTERN.matcher(pattern).matches()) { cString.getConstraint().remove(0); cString.getConstraint().add(pattern + ".*"); } @@ -412,18 +413,13 @@ private static void fixArchetypeSlotExpression(Expression expression) { } } - - - - + /** + * If the object has a nodeId + * - replace it with a new nodeId + * - store the old and new nodeId as a converted code + */ private void calculateNewNodeId(CObject cObject) { - if(cObject.getNodeId() == null) { - if(cObject instanceof CComplexObject || cObject instanceof CArchetypeRoot || cObject instanceof ArchetypeSlot) { - //will be calculated later - } else if(cObject instanceof CPrimitiveObject){ - //done automatically - } - } else { + if (cObject.getNodeId() != null) { String oldNodeId = cObject.getNodeId(); String newNodeId = convertNodeId(oldNodeId); addConvertedCode(oldNodeId, newNodeId); @@ -432,8 +428,11 @@ private void calculateNewNodeId(CObject cObject) { } } + /** + * Stores the old and new nodeIds in a map, so that all old nodeId can be converted into the same new nodeId + */ protected void addConvertedCode(String oldNodeId, String newNodeId) { - if(convertedCodes.containsKey(oldNodeId)) { + if (convertedCodes.containsKey(oldNodeId)) { convertedCodes.get(oldNodeId).addNewCode(newNodeId); } else { convertedCodes.put(oldNodeId, new ConvertedCodeResult(oldNodeId, newNodeId)); @@ -441,65 +440,75 @@ protected void addConvertedCode(String oldNodeId, String newNodeId) { this.newCodeToOldCodeMap.put(newNodeId, oldNodeId); } + /** + * Convert old code into an id code + */ public String convertNodeId(String oldNodeId) { ConvertedCodeResult convertedCodeResult = convertedCodes.get(oldNodeId); - if(convertedCodeResult != null && convertedCodeResult.hasIdCode()) { + if (convertedCodeResult != null && convertedCodeResult.hasIdCode()) { return convertedCodeResult.getIdCode(); } return convertCode(oldNodeId, "id"); } + /** + * Convert old code into an at code + */ protected String convertValueCode(String oldCode) { ConvertedCodeResult convertedCodeResult = convertedCodes.get(oldCode); - if(convertedCodeResult != null && convertedCodeResult.hasValueCode()) { + if (convertedCodeResult != null && convertedCodeResult.hasValueCode()) { return convertedCodeResult.getValueCode(); } return convertCode(oldCode, "at"); } + /** + * Convert old code into an ac code + */ public String convertValueSetCode(String oldCode) { ConvertedCodeResult convertedCodeResult = convertedCodes.get(oldCode); - if(convertedCodeResult != null && convertedCodeResult.hasValueCode()) { + if (convertedCodeResult != null && convertedCodeResult.hasValueCode()) { return convertedCodeResult.getValueCode(); } return convertCode(oldCode, "ac"); } + /** + * Convert old code into a code with prefix 'newCodePrefix' + * - Takes into account removing the leading zero's of the ADL1.4 codes + * - Takes into account that ADL1.4 starts with code 0 at the root and ADL2 starts with code 1 + */ public static String convertCode(String oldCode, String newCodePrefix) { NodeIdUtil nodeIdUtil = new NodeIdUtil(oldCode); if (nodeIdUtil.getCodes().isEmpty()) { return oldCode; } nodeIdUtil.setPrefix(newCodePrefix); //will automatically strip the leading zeroes due to integer-parsing - if(!oldCode.startsWith("at0.") && !oldCode.startsWith("ac0.")) { + if (!oldCode.startsWith("at0.") && !oldCode.startsWith("ac0.")) { //a bit tricky, since the root of an archetype starts with at0000.0, but that's different from this I guess nodeIdUtil.getCodes().set(0, nodeIdUtil.getCodes().get(0) + 1); //increment with 1, old is 0-based } return nodeIdUtil.toString(); } - private void convert(CAttribute attribute) { - for(CObject object:attribute.getChildren()) { - convert(object); - } - } - + /** + * Convert all old codes in a path in the new codes + */ public String convertPath(String key) { APathQuery aPathQuery = new APathQuery(key); - for(PathSegment segment:aPathQuery.getPathSegments()) { - if(segment.getNodeId() != null) { + for (PathSegment segment : aPathQuery.getPathSegments()) { + if (segment.getNodeId() != null) { segment.setNodeId(convertNodeId(segment.getNodeId())); } } return aPathQuery.toString(); } - public String getOldCodeForNewCode(String nodeId) { - return this.newCodeToOldCodeMap.get(nodeId); - } - - protected Archetype getFlatParentArchetype() { - return flatParentArchetype; + /** + * Given the new nodeId, return the old code of this object + */ + public String getOldCodeForNewCode(String newNodeId) { + return this.newCodeToOldCodeMap.get(newNodeId); } public ADL2ConversionResult getConversionResult() { diff --git a/aom/src/main/java/com/nedap/archie/adl14/ADL14Parser.java b/aom/src/main/java/com/nedap/archie/adl14/ADL14Parser.java index b032c3875..1e092e853 100644 --- a/aom/src/main/java/com/nedap/archie/adl14/ADL14Parser.java +++ b/aom/src/main/java/com/nedap/archie/adl14/ADL14Parser.java @@ -10,7 +10,7 @@ import com.nedap.archie.antlr.errors.ANTLRParserErrors; import com.nedap.archie.antlr.errors.ArchieErrorListener; import com.nedap.archie.aom.Archetype; -import com.nedap.archie.aom.utils.ArchetypeParsePostProcesser; +import com.nedap.archie.aom.utils.ArchetypeParsePostProcessor; import com.nedap.archie.rminfo.MetaModels; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; @@ -74,7 +74,7 @@ public Archetype parse(CharStream stream, ADL14ConversionConfiguration conversio walker = new ParseTreeWalker(); walker.walk(listener, tree); result = listener.getArchetype(); - ArchetypeParsePostProcesser.fixArchetype(result); + ArchetypeParsePostProcessor.fixArchetype(result); if (metaModels != null) { metaModels.selectModel(result); if (metaModels.getSelectedBmmModel() != null) { diff --git a/aom/src/main/java/com/nedap/archie/adl14/ADL2ConversionResult.java b/aom/src/main/java/com/nedap/archie/adl14/ADL2ConversionResult.java index 80aeb3654..5ed1bc82d 100644 --- a/aom/src/main/java/com/nedap/archie/adl14/ADL2ConversionResult.java +++ b/aom/src/main/java/com/nedap/archie/adl14/ADL2ConversionResult.java @@ -7,7 +7,7 @@ /** * ADL 2 conversion result. Always has the archetypeId field set. - * Either has archetype and conversionLog non-null in case of a succesful conversion, or + * Either has archetype and conversionLog non-null in case of a successful conversion, or * exception non-null in case of an unexpected Exception */ public class ADL2ConversionResult { @@ -18,24 +18,13 @@ public class ADL2ConversionResult { private MessageLogger log; private Exception exception; - /** - * empty constructor for JSON parsing. Do not use - */ public ADL2ConversionResult() { - + /* Empty construction for Jackson parsing */ } public ADL2ConversionResult(Archetype archetype) { - this.archetypeId = archetype.getArchetypeId().getFullId(); + this(archetype.getArchetypeId().getFullId(), null); this.archetype = archetype; - log = new MessageLogger(); - } - - public ADL2ConversionResult(Archetype archetype, ADL2ConversionLog conversionLog) { - this.archetypeId = archetype.getArchetypeId().getFullId(); - this.archetype = archetype; - this.conversionLog = conversionLog; - log = new MessageLogger(); } public ADL2ConversionResult(String archetypeId, Exception exception) { @@ -44,14 +33,11 @@ public ADL2ConversionResult(String archetypeId, Exception exception) { log = new MessageLogger(); } + /** GETTERS **/ public String getArchetypeId() { return archetypeId; } - public void setArchetypeId(String archetypeId) { - this.archetypeId = archetypeId; - } - public Archetype getArchetype() { return archetype; } @@ -60,14 +46,6 @@ public ADL2ConversionLog getConversionLog() { return conversionLog; } - public void setConversionLog(ADL2ConversionLog conversionLog) { - this.conversionLog = conversionLog; - } - - public void setArchetype(Archetype archetype) { - this.archetype = archetype; - } - public MessageLogger getLog() { return log; } @@ -76,9 +54,12 @@ public Exception getException() { return exception; } - public void setException(Exception exception) { - this.exception = exception; + /** SETTERS **/ + public void setArchetype(Archetype archetype) { + this.archetype = archetype; } - + public void setConversionLog(ADL2ConversionLog conversionLog) { + this.conversionLog = conversionLog; + } } diff --git a/aom/src/main/java/com/nedap/archie/adlparser/ADLParser.java b/aom/src/main/java/com/nedap/archie/adlparser/ADLParser.java index addc9f75d..5f02a17c7 100644 --- a/aom/src/main/java/com/nedap/archie/adlparser/ADLParser.java +++ b/aom/src/main/java/com/nedap/archie/adlparser/ADLParser.java @@ -8,7 +8,7 @@ import com.nedap.archie.antlr.errors.ArchieErrorListener; import com.nedap.archie.antlr.errors.ANTLRParserErrors; import com.nedap.archie.aom.Archetype; -import com.nedap.archie.aom.utils.ArchetypeParsePostProcesser; +import com.nedap.archie.aom.utils.ArchetypeParsePostProcessor; import com.nedap.archie.rminfo.MetaModels; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.*; @@ -97,7 +97,7 @@ public Archetype parse(CharStream stream) throws ADLParseException { walker.walk(listener, tree); result = listener.getArchetype(); //set some values that are not directly in ODIN or ADL - ArchetypeParsePostProcesser.fixArchetype(result); + ArchetypeParsePostProcessor.fixArchetype(result); if (modelConstraintImposer != null && result.getDefinition() != null) { modelConstraintImposer.imposeConstraints(result.getDefinition()); 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 583ecf964..8d85292f7 100644 --- a/aom/src/main/java/com/nedap/archie/aom/Archetype.java +++ b/aom/src/main/java/com/nedap/archie/aom/Archetype.java @@ -9,7 +9,7 @@ import com.nedap.archie.aom.terminology.ArchetypeTerminology; import com.nedap.archie.aom.terminology.ValueSet; import com.nedap.archie.aom.utils.AOMUtils; -import com.nedap.archie.aom.utils.ArchetypeParsePostProcesser; +import com.nedap.archie.aom.utils.ArchetypeParsePostProcessor; import com.nedap.archie.definitions.AdlCodeDefinitions; import com.nedap.archie.query.AOMPathQuery; import com.nedap.archie.rminfo.RMProperty; @@ -236,7 +236,7 @@ public Archetype clone() { Archetype result = (Archetype) super.clone(); //fix some things that are not handled automatically - ArchetypeParsePostProcesser.fixArchetype(result); + ArchetypeParsePostProcessor.fixArchetype(result); return result; } diff --git a/aom/src/main/java/com/nedap/archie/aom/utils/ArchetypeParsePostProcesser.java b/aom/src/main/java/com/nedap/archie/aom/utils/ArchetypeParsePostProcessor.java similarity index 76% rename from aom/src/main/java/com/nedap/archie/aom/utils/ArchetypeParsePostProcesser.java rename to aom/src/main/java/com/nedap/archie/aom/utils/ArchetypeParsePostProcessor.java index e36308764..b3cb6187f 100644 --- a/aom/src/main/java/com/nedap/archie/aom/utils/ArchetypeParsePostProcesser.java +++ b/aom/src/main/java/com/nedap/archie/aom/utils/ArchetypeParsePostProcessor.java @@ -16,10 +16,10 @@ /** * Sets some values that are not directly in ADL or ODIN, such as original language in terminology, etc. */ -public class ArchetypeParsePostProcesser { +public class ArchetypeParsePostProcessor { public static void fixArchetype(Archetype archetype) { - if(archetype.getTerminology() != null) { + if (archetype.getTerminology() != null) { ArchetypeTerminology terminology = archetype.getTerminology(); //codes are in model, but do not appear in odin. Set them here fillArchetypeTermCodes(terminology.getTermDefinitions()); @@ -37,33 +37,33 @@ public static void fixArchetype(Archetype archetype) { private static void setParents(Archetype archetype) { Stack workList = new Stack<>(); workList.add(archetype.getDefinition()); - while(!workList.empty()) { + while (!workList.empty()) { CObject cObject = workList.pop(); - if(cObject instanceof CPrimitiveObject) { + if (cObject instanceof CPrimitiveObject) { cObject.setNodeId("id9999");//also in the implementation, but check to be sure } - for(CAttribute attribute:cObject.getAttributes()) { + for (CAttribute attribute : cObject.getAttributes()) { attribute.setParent(cObject); - for(CObject child:attribute.getChildren()) { + for (CObject child : attribute.getChildren()) { child.setParent(attribute); } workList.addAll(attribute.getChildren()); } - if(cObject instanceof CComplexObject) { + if (cObject instanceof CComplexObject) { //TODO: fix tuple so it has proper multiple references to the separate structures, instead of a complete duplication of data in json CComplexObject cComplexObject = (CComplexObject) cObject; - for(CAttributeTuple tuple: cComplexObject.getAttributeTuples()) { - for(CAttribute attribute:tuple.getMembers()) { + for (CAttributeTuple tuple : cComplexObject.getAttributeTuples()) { + for (CAttribute attribute : tuple.getMembers()) { attribute.setSocParent(tuple); attribute.setParent(cObject); workList.addAll(attribute.getChildren()); cComplexObject.replaceAttribute(attribute); } - for(CPrimitiveTuple primitiveTuple:tuple.getTuples()) { + for (CPrimitiveTuple primitiveTuple : tuple.getTuples()) { int index = 0; - for(CPrimitiveObject object:primitiveTuple.getMembers()) { - if(index < tuple.getMembers().size()) { + for (CPrimitiveObject object : primitiveTuple.getMembers()) { + if (index < tuple.getMembers().size()) { CAttribute attribute = tuple.getMember(index); object.setSocParent(primitiveTuple); object.setParent(attribute); @@ -79,9 +79,9 @@ private static void setParents(Archetype archetype) { } private static void fillArchetypeTermCodes(Map> termSet) { - if(termSet != null) { - for(Map language:termSet.values()) { - for(String term:language.keySet()) { + if (termSet != null) { + for (Map language : termSet.values()) { + for (String term : language.keySet()) { language.get(term).setCode(term); } } diff --git a/tools/src/main/java/com/nedap/archie/adl14/ADL14Converter.java b/tools/src/main/java/com/nedap/archie/adl14/ADL14Converter.java index 90a03d055..c816c628e 100644 --- a/tools/src/main/java/com/nedap/archie/adl14/ADL14Converter.java +++ b/tools/src/main/java/com/nedap/archie/adl14/ADL14Converter.java @@ -2,13 +2,11 @@ import com.nedap.archie.adl14.log.ADL2ConversionLog; import com.nedap.archie.adl14.log.ADL2ConversionRunLog; -import com.nedap.archie.terminology.OpenEHRTerminologyAccess; -import com.nedap.archie.terminology.TerminologyAccess; import com.nedap.archie.aom.Archetype; import com.nedap.archie.aom.ResourceDescription; import com.nedap.archie.aom.Template; import com.nedap.archie.aom.TemplateOverlay; -import com.nedap.archie.aom.utils.ArchetypeParsePostProcesser; +import com.nedap.archie.aom.utils.ArchetypeParsePostProcessor; import com.nedap.archie.diff.Differentiator; import com.nedap.archie.flattener.Flattener; import com.nedap.archie.flattener.InMemoryFullArchetypeRepository; @@ -23,48 +21,30 @@ public class ADL14Converter { private final MetaModels metaModels; - private TerminologyAccess terminologyAccess = OpenEHRTerminologyAccess.getInstance(); private final ADL14ConversionConfiguration conversionConfiguration; - private InMemoryFullArchetypeRepository existingRepository; public ADL14Converter(MetaModels metaModels, ADL14ConversionConfiguration conversionConfiguration) { this.metaModels = metaModels; this.conversionConfiguration = conversionConfiguration; } - - /** - * Set the base repository for this converter. If you don't set it yourself, the converter will create an empty repository. - * The archetypes in the repository will be used as base archetypes, but will not be converted themselves. Please - * note that this repository will be modified, it will have all the converted archetypes added to it! - * - * @param existingRepository the existing repository to use - */ - public void setExistingRepository(InMemoryFullArchetypeRepository existingRepository) { - this.existingRepository = existingRepository; - } - public ADL2ConversionResultList convert(List archetypes) { return convert(archetypes, null); } public ADL2ConversionResultList convert(List archetypes, ADL2ConversionRunLog previousConversion) { ADL2ConversionResultList resultList = new ADL2ConversionResultList(); - - InMemoryFullArchetypeRepository repository = existingRepository; - if(repository == null) { - repository = new InMemoryFullArchetypeRepository(); - } + InMemoryFullArchetypeRepository repository = new InMemoryFullArchetypeRepository(); List unprocessed = new ArrayList<>(archetypes); + // ADL 1.4 does not really have templates. This code is here for the Better Care template conversion + // also it can be used to write our own template converter later. + // So add the overlays in the right order here List templateOverlays = new ArrayList<>(); - for(Archetype ar:unprocessed) { - //ADL 1.4 does not really have templates. This code is here for the Better Care template conversion - //also it can be used to write our own template converter later. - //So add the overlays in the right order here - if(ar instanceof Template) { - Template t = (Template) ar; - for(TemplateOverlay overlay:t.getTemplateOverlays()) { + for (Archetype archetype : unprocessed) { + if (archetype instanceof Template) { + Template t = (Template) archetype; + for (TemplateOverlay overlay : t.getTemplateOverlays()) { templateOverlays.add(overlay); overlay.setRmRelease(t.getRmRelease()); } @@ -72,24 +52,22 @@ public ADL2ConversionResultList convert(List archetypes, ADL2Conversi } unprocessed.addAll(templateOverlays); - //process the archetypes ordered by specialization level - unprocessed.sort(Comparator.comparingInt(a -> a.specializationDepth())); + // Process the archetypes ordered by specialization level + unprocessed.sort(Comparator.comparingInt(Archetype::specializationDepth)); Differentiator differentiator = new Differentiator(metaModels); - for(Archetype archetype:unprocessed) { - - ADL2ConversionResult result = null; + for (Archetype archetype : unprocessed) { + ADL2ConversionResult result; try { if (archetype.getParentArchetypeId() != null) { - Archetype parent = repository.getArchetype(archetype.getParentArchetypeId()); - if(parent == null) { + if (parent == null) { throw new RuntimeException(MessageFormat.format("Cannot find parent {0} for archetype {1}", archetype.getParentArchetypeId(), archetype.getArchetypeId())); } Archetype flatParent = new Flattener(repository, metaModels).flatten(parent); result = convert(archetype, flatParent, previousConversion); if (result.getArchetype() != null) { - if(conversionConfiguration.isApplyDiff()) { + if (conversionConfiguration.isApplyDiff()) { result.setArchetype(differentiator.differentiate(result.getArchetype(), flatParent, true)); } else { result.setArchetype(differentiator.differentiate(result.getArchetype(), flatParent, false)); @@ -100,7 +78,7 @@ public ADL2ConversionResultList convert(List archetypes, ADL2Conversi result = convert(archetype, previousConversion); resultList.addConversionResult(result); } - if(result != null && result.getArchetype() != null && result.getArchetype().getArchetypeId() != null) { + if (result.getArchetype() != null && result.getArchetype().getArchetypeId() != null) { repository.addArchetype(result.getArchetype()); } } catch (Exception e) { @@ -108,76 +86,82 @@ public ADL2ConversionResultList convert(List archetypes, ADL2Conversi resultList.addConversionResult(result); } } - - return resultList; } - - private ADL2ConversionResult convert(Archetype archetype, ADL2ConversionRunLog previousConversion) { + private ADL2ConversionResult convert(Archetype archetype, ADL2ConversionRunLog previousConversion) { return convert(archetype, null, previousConversion); } private ADL2ConversionResult convert(Archetype archetype, Archetype flatParent, ADL2ConversionRunLog previousConversion) { ADL2ConversionLog previousLog = previousConversion == null ? null : previousConversion.getConversionLog(archetype.getArchetypeId().getSemanticId()); Archetype convertedArchetype = archetype.clone(); + + // Convert description section new ADL14DescriptionConverter().convert(convertedArchetype); + + // Convert or correct adl, rm and archetypeId versions setCorrectVersions(convertedArchetype); + + // Convert header section convertHeader(convertedArchetype); - addDefaultMultiplicities(convertedArchetype); + // Correct default multiplicities + new ADL14DefaultMultiplicitiesSetter(metaModels).setDefaults(convertedArchetype); + // Convert nodeId's ADL2ConversionResult result = new ADL2ConversionResult(convertedArchetype); ADL14NodeIDConverter adl14NodeIDConverter = new ADL14NodeIDConverter(this.metaModels, convertedArchetype, flatParent, conversionConfiguration, previousLog, result); - ADL2ConversionLog conversionLog = adl14NodeIDConverter.convert();//fixes archetype in place + ADL2ConversionLog conversionLog = adl14NodeIDConverter.convert(); result.setConversionLog(conversionLog); - //ADL 1.4 has cardinality, existence and occurrences always present, in ADL 2 they can be removed if same as default. - //so remove them + // Remove structures that are not default in ADL1.4, but are default in ADL2 new DefaultRmStructureRemover(metaModels, true).removeRMDefaults(convertedArchetype); - //set some values that are not directly in ODIN or ADL - ArchetypeParsePostProcesser.fixArchetype(convertedArchetype); + + // Set some values that are not directly in ODIN or ADL + ArchetypeParsePostProcessor.fixArchetype(convertedArchetype); return result; + } + /** + * Set correct ADL and RM version for the archetype + * Add minor and patch version to archetypeId if minor version is not set in ADL1.4 + */ + private void setCorrectVersions(Archetype convertedArchetype) { + convertedArchetype.setAdlVersion("2.0.6"); + convertedArchetype.setRmRelease(conversionConfiguration.getRmRelease()); + if (convertedArchetype.getArchetypeId().getMinorVersion() == null) { + convertedArchetype.getArchetypeId().setReleaseVersion(convertedArchetype.getArchetypeId().getReleaseVersion() + ".0.0"); + } } + /** + * Move Uid and BuildUid to OtherDetails oid and build_oid respectively + */ private void convertHeader(Archetype convertedArchetype) { - if(convertedArchetype.getUid() != null) { - //if UID is in OID syntax, move it to the description - if(convertedArchetype.getUid().matches("[0-9]+(\\.[0-9]+)+")) { + if (convertedArchetype.getUid() != null) { + // If UID is in OID syntax, move it to the description + if (convertedArchetype.getUid().matches("[0-9]+(\\.[0-9]+)+")) { moveOidToMetadata(convertedArchetype, convertedArchetype.getUid(), "oid"); convertedArchetype.setUid(null); } } - if(convertedArchetype.getBuildUid() != null) { - if(convertedArchetype.getBuildUid().matches("[0-9]+\\.([0-9]+)+")) { + if (convertedArchetype.getBuildUid() != null) { + if (convertedArchetype.getBuildUid().matches("[0-9]+\\.([0-9]+)+")) { moveOidToMetadata(convertedArchetype, convertedArchetype.getBuildUid(), "build_oid"); convertedArchetype.setBuildUid(null); } } } - private void addDefaultMultiplicities(Archetype convertedArchetype) { - new ADL14DefaultMultiplicitiesSetter(metaModels).setDefaults(convertedArchetype); - } - private void moveOidToMetadata(Archetype convertedArchetype, String oid, String oidFieldName) { - if(convertedArchetype.getDescription() == null) { + if (convertedArchetype.getDescription() == null) { convertedArchetype.setDescription(new ResourceDescription()); } - if(convertedArchetype.getDescription().getOtherDetails() == null) { + if (convertedArchetype.getDescription().getOtherDetails() == null) { convertedArchetype.getDescription().setOtherDetails(new LinkedHashMap<>()); } convertedArchetype.getDescription().getOtherDetails().put(oidFieldName, oid); } - - - private void setCorrectVersions(Archetype result) { - result.setAdlVersion("2.0.6"); - result.setRmRelease(conversionConfiguration.getRmRelease()); - if(result.getArchetypeId().getMinorVersion() == null) { - result.getArchetypeId().setReleaseVersion(result.getArchetypeId().getReleaseVersion() + ".0.0"); - } - } } diff --git a/tools/src/main/java/com/nedap/archie/adl14/DefaultRmStructureRemover.java b/tools/src/main/java/com/nedap/archie/adl14/DefaultRmStructureRemover.java index 9298eedc8..3e423412e 100644 --- a/tools/src/main/java/com/nedap/archie/adl14/DefaultRmStructureRemover.java +++ b/tools/src/main/java/com/nedap/archie/adl14/DefaultRmStructureRemover.java @@ -17,28 +17,19 @@ *

- * */ public class DefaultRmStructureRemover { private final MetaModels metaModels; + private final boolean removeEmptyAttributes; private BMMConstraintImposer constraintImposer; - private boolean removeEmptyAttributes = false; - - /** - * Construct a DefaultRmStructureRemover that does not remove empty attributes - * @param metaModels the metamodels containing metamodel information for the preseted archetypes - */ - public DefaultRmStructureRemover(MetaModels metaModels) { - this(metaModels, false); - } - /** * Construct a DefaultRmStructureRemover - * @param metaModels the metamodels containing metamodel information for the preseted archetypes + * + * @param metaModels the metamodels containing metamodel information for the preset archetypes * @param removeEmptyAttributes if true, will remove empty attributes. If false, will not */ public DefaultRmStructureRemover(MetaModels metaModels, boolean removeEmptyAttributes) { @@ -46,35 +37,32 @@ public DefaultRmStructureRemover(MetaModels metaModels, boolean removeEmptyAttri this.removeEmptyAttributes = removeEmptyAttributes; } - public void setRemoveEmptyAttributes(boolean removeEmptyAttributes) { - this.removeEmptyAttributes = removeEmptyAttributes; - } - public void removeRMDefaults(Archetype archetype) { this.metaModels.selectModel(archetype); - if(metaModels.getSelectedModel() == null) { + if (metaModels.getSelectedModel() == null) { throw new IllegalArgumentException("cannot find model for argument, so cannot remove default multiplicity"); } - this.constraintImposer = new BMMConstraintImposer(metaModels.getSelectedBmmModel()); removeRMDefaults(archetype.getDefinition()); } private void removeRMDefaults(CObject object) { - if(object.getOccurrences() != null) { + // Remove occurrences if they are equal to the default occurrences of the object + if (object.getOccurrences() != null) { MultiplicityInterval defaultRMOccurrences = object.getDefaultRMOccurrences(metaModels::referenceModelPropMultiplicity); - if(defaultRMOccurrences.equals(object.getOccurrences())) { + if (defaultRMOccurrences.equals(object.getOccurrences())) { object.setOccurrences(null); } } - if(object instanceof CComplexObject) { - CComplexObject complexObject = (CComplexObject) object; + // Remove default multiplicities of attributes + if (object instanceof CComplexObject) { + CComplexObject complexObject = (CComplexObject) object; List attributesToRemove = new ArrayList<>(); for (CAttribute attribute : object.getAttributes()) { removeMultiplicities(attribute); - if(removeEmptyAttributes) { - //remove all empty attributes. They are 'attribute matches {*}' in ADL 1.4, and should not be present in ADL 2 + if (removeEmptyAttributes) { + // Remove all empty attributes. They are 'attribute matches {*}' in ADL 1.4, and should not be present in ADL 2 if (attribute.getCardinality() == null && attribute.getExistence() == null && (attribute.getChildren() == null || attribute.getChildren().isEmpty())) { if (!isInTuple(complexObject, attribute)) { attributesToRemove.add(attribute); @@ -82,43 +70,43 @@ private void removeRMDefaults(CObject object) { } } } - for (CAttribute attributeToRemove : attributesToRemove) { complexObject.removeAttribute(attributeToRemove); } } - - } - - private boolean isInTuple(CComplexObject complexObject, CAttribute attribute) { - if(complexObject.getAttributeTuples() == null) { - return false; - } - for(CAttributeTuple tuple:complexObject.getAttributeTuples()) { - if(tuple.getMember(attribute.getRmAttributeName()) != null) { - return true; - } - } - return false; } private void removeMultiplicities(CAttribute attribute) { + // Remove existence and cardinality if they are equal to the default existence and cardinality of the attribute CAttribute defaultAttribute = constraintImposer.getDefaultAttribute(attribute.getParent().getRmTypeName(), attribute.getRmAttributeName()); - if(attribute.getExistence() != null) { - if(defaultAttribute != null && defaultAttribute.getExistence() != null && defaultAttribute.getExistence().equals(attribute.getExistence())) { + if (attribute.getExistence() != null) { + if (defaultAttribute != null && defaultAttribute.getExistence() != null && defaultAttribute.getExistence().equals(attribute.getExistence())) { attribute.setExistence(null); } } - if(attribute.getCardinality() != null) { - if(defaultAttribute != null && defaultAttribute.getCardinality() != null) { - if(defaultAttribute.getCardinality().equals(attribute.getCardinality())) { + if (attribute.getCardinality() != null) { + if (defaultAttribute != null && defaultAttribute.getCardinality() != null) { + if (defaultAttribute.getCardinality().equals(attribute.getCardinality())) { attribute.setCardinality(null); } } } - for(CObject child:attribute.getChildren()) { + + // Remove default occurrences of child objects + for (CObject child : attribute.getChildren()) { removeRMDefaults(child); } + } + private boolean isInTuple(CComplexObject complexObject, CAttribute attribute) { + if (complexObject.getAttributeTuples() == null) { + return false; + } + for (CAttributeTuple tuple : complexObject.getAttributeTuples()) { + if (tuple.getMember(attribute.getRmAttributeName()) != null) { + return true; + } + } + return false; } } 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 b5d0ccc6a..bec34e4f1 100644 --- a/tools/src/main/java/com/nedap/archie/flattener/Flattener.java +++ b/tools/src/main/java/com/nedap/archie/flattener/Flattener.java @@ -2,7 +2,7 @@ import com.nedap.archie.adlparser.modelconstraints.ReflectionConstraintImposer; import com.nedap.archie.aom.*; -import com.nedap.archie.aom.utils.ArchetypeParsePostProcesser; +import com.nedap.archie.aom.utils.ArchetypeParsePostProcessor; import com.nedap.archie.rminfo.MetaModels; import com.nedap.archie.rminfo.ReferenceModels; import org.openehr.bmm.v2.validation.BmmRepository; @@ -220,7 +220,7 @@ public Archetype flatten(Archetype toFlatten) { flatOverlay.setDescription(description); flatOverlay.setOriginalLanguage(result.getOriginalLanguage()); flatOverlay.setTranslationList(result.getTranslationList()); - ArchetypeParsePostProcesser.fixArchetype(flatOverlay); + ArchetypeParsePostProcessor.fixArchetype(flatOverlay); resultTemplate.getTemplateOverlays().add(flatOverlay); } } @@ -230,7 +230,7 @@ public Archetype flatten(Archetype toFlatten) { result.setDifferential(false);//mark this archetype as being flat result.setGenerated(true); - ArchetypeParsePostProcesser.fixArchetype(result); + ArchetypeParsePostProcessor.fixArchetype(result); //set the single/multiple attributes correctly new ReflectionConstraintImposer(metaModels.getSelectedModel()) diff --git a/tools/src/test/java/com/nedap/archie/json/flat/ArchetypeParsePostProcessorTest.java b/tools/src/test/java/com/nedap/archie/json/flat/ArchetypeParsePostProcessorTest.java index bb7c04287..9636abb0a 100644 --- a/tools/src/test/java/com/nedap/archie/json/flat/ArchetypeParsePostProcessorTest.java +++ b/tools/src/test/java/com/nedap/archie/json/flat/ArchetypeParsePostProcessorTest.java @@ -1,7 +1,7 @@ package com.nedap.archie.json.flat; import com.nedap.archie.aom.*; -import com.nedap.archie.aom.utils.ArchetypeParsePostProcesser; +import com.nedap.archie.aom.utils.ArchetypeParsePostProcessor; import com.nedap.archie.json.ArchieJacksonConfiguration; import com.nedap.archie.json.JacksonUtil; import org.junit.Test; @@ -16,7 +16,7 @@ public void setTupleParents() throws Exception { ArchieJacksonConfiguration config = ArchieJacksonConfiguration.createConfigForJavascriptUsage(); try(InputStream stream = getClass().getResourceAsStream("/com/nedap/archie/json/snaq_rc_opt.js")) { OperationalTemplate template = JacksonUtil.getObjectMapper(config).readValue(stream, OperationalTemplate.class); - ArchetypeParsePostProcesser.fixArchetype(template); + ArchetypeParsePostProcessor.fixArchetype(template); CComplexObject dvOrdinal = template.itemAtPath("/content[id0.0.100.1]/data[id2]/events[id3]/data[id4]/items[id15]/value[id25]"); CAttributeTuple tuple = dvOrdinal.getAttributeTuples().get(0); for(CAttribute tupleMember:tuple.getMembers()) {