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 ead22a1eb..0e667efb3 100644
--- a/aom/src/main/java/com/nedap/archie/adl14/ADL14NodeIDConverter.java
+++ b/aom/src/main/java/com/nedap/archie/adl14/ADL14NodeIDConverter.java
@@ -396,8 +396,7 @@ 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().isEmpty() &&
- constraint.getItem() instanceof CString) {
+ if (constraint.getItem() != null && constraint.getItem() instanceof CString && constraint.getItem().getConstraint() != null && !((CString) constraint.getItem()).getConstraint().isEmpty()) {
CString cString = (CString) constraint.getItem();
if (cString.getConstraint() == null || cString.getConstraint().isEmpty()) {
return;
diff --git a/aom/src/main/java/com/nedap/archie/adl14/ADL14TermConstraintConverter.java b/aom/src/main/java/com/nedap/archie/adl14/ADL14TermConstraintConverter.java
index c76bffff4..d75d6dd97 100644
--- a/aom/src/main/java/com/nedap/archie/adl14/ADL14TermConstraintConverter.java
+++ b/aom/src/main/java/com/nedap/archie/adl14/ADL14TermConstraintConverter.java
@@ -1,6 +1,5 @@
package com.nedap.archie.adl14;
-import com.google.common.collect.Lists;
import com.nedap.archie.adl14.log.CreatedCode;
import com.nedap.archie.adl14.log.ReasonForCodeCreation;
import com.nedap.archie.aom.*;
@@ -56,8 +55,8 @@ private void convert(CObject cObject) {
for (CPrimitiveObject, ?> cPrimitiveObject : termCodes) {
CTerminologyCode cTerminologyCode = (CTerminologyCode) cPrimitiveObject;
convertCTerminologyCode(cTerminologyCode);
- if(cTerminologyCode.getConstraint().size() == 1) {
- String constraint = cTerminologyCode.getConstraint().get(0);
+ if(cTerminologyCode.getConstraint() != null) {
+ String constraint = cTerminologyCode.getConstraint();
if(AOMUtils.isValueCode(constraint)) {
atCodes.add(constraint);
}
@@ -91,103 +90,132 @@ private void convert(CAttribute attribute) {
}
}
+ /**
+ * Converts a single-code {@link CTerminologyCode} constraint from ADL 1.4 to ADL 2 format.
+ * Multi-code constraints (stored via {@link CTerminologyCode#getPendingCodes()} by the ADL 1.4 parser)
+ * are delegated to {@link #convertMultiCodeTerminologyCode}.
+ *
+ * Three cases are handled:
+ *
+ * - Local at-code (e.g. {@code at0001}): converted to its ADL 2 equivalent.
+ * - Local ac-code (e.g. {@code ac0001}): converted to its ADL 2 value-set code equivalent.
+ * - External terminology code (e.g. {@code [snomed-ct::12345]}): a term binding is created and
+ * a new local at-code is generated to reference it.
+ *
+ */
private void convertCTerminologyCode(CTerminologyCode cTerminologyCode) {
- if(cTerminologyCode.getConstraint() != null && !cTerminologyCode.getConstraint().isEmpty()) {
- String firstConstraint = cTerminologyCode.getConstraint().get(0);
- TerminologyCode termCode = TerminologyCode.createFromString(firstConstraint);
- boolean isLocalCode = termCode.getTerminologyId() == null || termCode.getTerminologyId().equalsIgnoreCase("local");
- if(isLocalCode && AOMUtils.isValueCode(firstConstraint)) {
- //local codes
- if(cTerminologyCode.getConstraint().size() == 1) {
- //do not create a value set, just convert the code
- String newCode = converter.convertValueCode(firstConstraint);
- converter.addConvertedCode(firstConstraint, newCode);
- cTerminologyCode.setConstraint(Lists.newArrayList(newCode));
- } else {
- Set localCodes = new LinkedHashSet<>();
- for(String code:cTerminologyCode.getConstraint()) {
- String newCode = converter.convertValueCode(code);
- converter.addConvertedCode(code, newCode);
- localCodes.add(newCode);
- }
+ List pendingCodes = cTerminologyCode.getPendingCodes();
+ if (pendingCodes != null && !pendingCodes.isEmpty()) {
+ convertMultiCodeTerminologyCode(cTerminologyCode, pendingCodes);
+ return;
+ }
- ValueSet valueSet = findOrCreateValueSet(cTerminologyCode.getArchetype(), localCodes, cTerminologyCode);
- cTerminologyCode.setConstraint(Lists.newArrayList(valueSet.getId()));
- }
- } else if (isLocalCode && AOMUtils.isValueSetCode(termCode.getCodeString())) {
- List newConstraint = new ArrayList<>();
- for(String constraint:cTerminologyCode.getConstraint()) {
- TerminologyCode code = TerminologyCode.createFromString(constraint);
- String newCode = converter.convertValueSetCode(code.getCodeString());
- converter.addConvertedCode(termCode.getCodeString(), newCode);
- newConstraint.add(newCode);
- }
- cTerminologyCode.setConstraint(newConstraint);
-
- } else {
- if (cTerminologyCode.getConstraint().size() == 1) {
- try {
- //do not create a value set, create a code plus binding to the old non-local code
- URI uri = new ADL14ConversionUtil(converter.getConversionConfiguration()).convertToUri(termCode);
- Map termBindingsMap = findOrCreateTermBindings(termCode);
-
- //TODO: check if this is a converted or old term binding - old is unusual, but could be possible!
- String termBinding = findOrAddTermBindingAndCode(termCode, uri, termBindingsMap);
- cTerminologyCode.setConstraint(Lists.newArrayList(termBinding));
- } catch (URISyntaxException e) {
- //TODO
- logger.error("error converting term", e);
- }
- } else {
- String terminologyId = cTerminologyCode.getConstraint().get(0);
- termCode = TerminologyCode.createFromString(terminologyId, null, cTerminologyCode.getConstraint().get(1));
- Map termBindingsMap = findOrCreateTermBindings(termCode);
- List atCodes = new ArrayList<>();
- List constraints = new ArrayList<>(cTerminologyCode.getConstraint());
- cTerminologyCode.setConstraint(atCodes);
- for(int i = 1; i < constraints.size(); i++) {
- String constraint = constraints.get(i);
- try {
- if(constraint.startsWith("[") && constraint.endsWith("]")) {
- TerminologyCode constraintCode = TerminologyCode.createFromString(constraint);
- URI uri = new ADL14ConversionUtil(converter.getConversionConfiguration()).convertToUri(constraintCode);
- atCodes.add(findOrAddTermBindingAndCode(constraintCode, uri, termBindingsMap));
- } else {
- TerminologyCode constraintCode = new TerminologyCode();
- constraintCode.setTerminologyId(terminologyId);
- constraintCode.setCodeString(constraint);
- URI uri = new ADL14ConversionUtil(converter.getConversionConfiguration()).convertToUri(constraintCode);
- atCodes.add(findOrAddTermBindingAndCode(constraintCode, uri, termBindingsMap));
- }
+ String constraint = cTerminologyCode.getConstraint();
+ if (constraint == null) {
+ return;
+ }
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }
- }
- ValueSet valueSet = findOrCreateValueSet(cTerminologyCode.getArchetype(), new LinkedHashSet<>(atCodes), cTerminologyCode);
- cTerminologyCode.setConstraint(Lists.newArrayList(valueSet.getId()));
+ TerminologyCode termCode = TerminologyCode.createFromString(constraint);
+ boolean isLocalCode = termCode.getTerminologyId() == null
+ || termCode.getTerminologyId().equalsIgnoreCase("local");
- }
+ if (isLocalCode && AOMUtils.isValueCode(constraint)) {
+ // Single local at-code: convert it to its ADL 2 equivalent
+ String newCode = converter.convertValueCode(constraint);
+ converter.addConvertedCode(constraint, newCode);
+ cTerminologyCode.setConstraint(newCode);
+ } else if (isLocalCode && AOMUtils.isValueSetCode(termCode.getCodeString())) {
+ // Local value-set reference: convert the ac-code to its ADL 2 equivalent
+ String newCode = converter.convertValueSetCode(termCode.getCodeString());
+ converter.addConvertedCode(termCode.getCodeString(), newCode);
+ cTerminologyCode.setConstraint(newCode);
+ } else {
+ // External terminology: create a term binding and generate a new at-code to reference it
+ try {
+ URI uri = new ADL14ConversionUtil(converter.getConversionConfiguration()).convertToUri(termCode);
+ Map termBindingsMap = findOrCreateTermBindings(termCode);
+ cTerminologyCode.setConstraint(findOrAddTermBindingAndCode(termCode, uri, termBindingsMap));
+ } catch (URISyntaxException e) {
+ logger.error("error converting term", e);
}
- if(cTerminologyCode.getAssumedValue() != null) {
- TerminologyCode assumedValue = cTerminologyCode.getAssumedValue();
- if(isLocalCode) {
- String newCode = converter.convertValueCode(assumedValue.getCodeString());
- assumedValue.setCodeString(newCode);
- assumedValue.setTerminologyId(null);
- } else {
- try {
- Map termBindingsMap = findOrCreateTermBindings(assumedValue);
- URI uri = new ADL14ConversionUtil(converter.getConversionConfiguration()).convertToUri(assumedValue);
- assumedValue.setCodeString(findOrAddTermBindingAndCode(assumedValue, uri, termBindingsMap));
- assumedValue.setTerminologyId(null);
- assumedValue.setTerminologyVersion(null);
- } catch (URISyntaxException e) {
- //TODO
- e.printStackTrace();
- }
+ }
+
+ convertAssumedValue(cTerminologyCode, isLocalCode);
+ }
+
+ /**
+ * Converts a multi-code {@link CTerminologyCode} constraint from ADL 1.4 to ADL 2 format.
+ * In ADL 1.4, a constraint may reference multiple codes inline (e.g.
+ * {@code [local::at0001, at0002]} or {@code [snomed-ct::12345, 67890]}). ADL 2 represents
+ * these as a value set (ac-code), which this method creates.
+ *
+ * The {@code pendingCodes} list was populated by the ADL 1.4 parser:
+ *
+ * - For local codes: raw at-codes, e.g. {@code ["at0001", "at0002"]}.
+ * - For external codes: full term code refs normalised by the parser,
+ * e.g. {@code ["[snomed-ct::12345]", "[snomed-ct::67890]"]}.
+ *
+ * In both cases a value set is created and the constraint is set to its ac-code.
+ */
+ private void convertMultiCodeTerminologyCode(CTerminologyCode cTerminologyCode, List pendingCodes) {
+ TerminologyCode firstCode = TerminologyCode.createFromString(pendingCodes.get(0));
+ boolean isLocalCode = firstCode.getTerminologyId() == null
+ || firstCode.getTerminologyId().equalsIgnoreCase("local");
+
+ if (isLocalCode) {
+ // Convert each at-code and group them into a new value set
+ Set convertedCodes = new LinkedHashSet<>();
+ for (String code : pendingCodes) {
+ String newCode = converter.convertValueCode(code);
+ converter.addConvertedCode(code, newCode);
+ convertedCodes.add(newCode);
+ }
+ ValueSet valueSet = findOrCreateValueSet(cTerminologyCode.getArchetype(), convertedCodes, cTerminologyCode);
+ cTerminologyCode.setConstraint(valueSet.getId());
+ } else {
+ // Create a term binding for each external code, then group the resulting at-codes into a value set
+ Map termBindingsMap = findOrCreateTermBindings(firstCode);
+ List atCodes = new ArrayList<>();
+ for (String code : pendingCodes) {
+ TerminologyCode termCode = TerminologyCode.createFromString(code);
+ try {
+ URI uri = new ADL14ConversionUtil(converter.getConversionConfiguration()).convertToUri(termCode);
+ atCodes.add(findOrAddTermBindingAndCode(termCode, uri, termBindingsMap));
+ } catch (URISyntaxException e) {
+ logger.error("error converting term", e);
}
}
+ if (!atCodes.isEmpty()) {
+ ValueSet valueSet = findOrCreateValueSet(cTerminologyCode.getArchetype(), new LinkedHashSet<>(atCodes), cTerminologyCode);
+ cTerminologyCode.setConstraint(valueSet.getId());
+ }
+ }
+
+ convertAssumedValue(cTerminologyCode, isLocalCode);
+ }
+
+ /**
+ * Converts the assumed value of a {@link CTerminologyCode} from ADL 1.4 to ADL 2 format,
+ * mirroring the logic used for the constraint itself.
+ */
+ private void convertAssumedValue(CTerminologyCode cTerminologyCode, boolean isLocalCode) {
+ if (cTerminologyCode.getAssumedValue() == null) {
+ return;
+ }
+ TerminologyCode assumedValue = cTerminologyCode.getAssumedValue();
+ if (isLocalCode) {
+ assumedValue.setCodeString(converter.convertValueCode(assumedValue.getCodeString()));
+ assumedValue.setTerminologyId(null);
+ } else {
+ try {
+ Map termBindingsMap = findOrCreateTermBindings(assumedValue);
+ URI uri = new ADL14ConversionUtil(converter.getConversionConfiguration()).convertToUri(assumedValue);
+ assumedValue.setCodeString(findOrAddTermBindingAndCode(assumedValue, uri, termBindingsMap));
+ assumedValue.setTerminologyId(null);
+ assumedValue.setTerminologyVersion(null);
+ } catch (URISyntaxException e) {
+ logger.error("error converting term", e);
+ }
}
}
@@ -250,9 +278,9 @@ private ValueSet findOrCreateValueSet(Archetype archetype, Set localCode
CObject cObject = cAttributeInParent.getChildren().get(0);
if(cObject instanceof CTerminologyCode) {
CTerminologyCode termCodeInParent = (CTerminologyCode) cObject;
- if(termCodeInParent.getConstraint() != null && !termCodeInParent.getConstraint().isEmpty()) {
- if(termCodeInParent.getConstraint().get(0).startsWith("ac")) {
- idInparent = termCodeInParent.getConstraint().get(0);
+ if(termCodeInParent.getConstraint() != null) {
+ if(termCodeInParent.getConstraint().startsWith("ac")) {
+ idInparent = termCodeInParent.getConstraint();
}
}
}
diff --git a/aom/src/main/java/com/nedap/archie/adl14/PreviousConversionApplier.java b/aom/src/main/java/com/nedap/archie/adl14/PreviousConversionApplier.java
index 473f6de6c..89870fca8 100644
--- a/aom/src/main/java/com/nedap/archie/adl14/PreviousConversionApplier.java
+++ b/aom/src/main/java/com/nedap/archie/adl14/PreviousConversionApplier.java
@@ -199,7 +199,8 @@ private Set gatherUsedValueSets(CAttribute attribute) {
Set result = new LinkedHashSet<>();
for(CObject child:attribute.getChildren()) {
if(child instanceof CTerminologyCode) {
- for(String constraint:((CTerminologyCode) child).getConstraint()) {
+ String constraint = ((CTerminologyCode) child).getConstraint();
+ if(constraint != null) {
if(constraint.startsWith("ac")) {
result.add(constraint);
}
@@ -215,7 +216,7 @@ private Set gatherUsedValueSets(CAttribute attribute) {
for(CPrimitiveTuple primitiveTuple:tuple.getTuples()) {
CTerminologyCode cTermCode = (CTerminologyCode) primitiveTuple.getMember(symbolIndex);
if(cTermCode != null) {
- atCodes.addAll(cTermCode.getConstraint());
+ atCodes.addAll(cTermCode.getConstraintAsList());
}
}
}
diff --git a/aom/src/main/java/com/nedap/archie/adl14/aom14/CDVOrdinalItem.java b/aom/src/main/java/com/nedap/archie/adl14/aom14/CDVOrdinalItem.java
index 8f0f3ed99..771c80009 100644
--- a/aom/src/main/java/com/nedap/archie/adl14/aom14/CDVOrdinalItem.java
+++ b/aom/src/main/java/com/nedap/archie/adl14/aom14/CDVOrdinalItem.java
@@ -6,8 +6,6 @@
import com.nedap.archie.base.Interval;
import com.nedap.archie.base.terminology.TerminologyCode;
-import java.util.Arrays;
-
public class CDVOrdinalItem {
private Integer value;
@@ -35,7 +33,7 @@ public CTerminologyCode getSymbolAdl2() {
return null;
}
CTerminologyCode result = new CTerminologyCode();
- result.setConstraint(Arrays.asList(symbol.toString()));
+ result.setConstraint(symbol.toString());
return result;
}
diff --git a/aom/src/main/java/com/nedap/archie/adl14/treewalkers/Adl14CComplexObjectParser.java b/aom/src/main/java/com/nedap/archie/adl14/treewalkers/Adl14CComplexObjectParser.java
index 26f8cbfd0..2d2290808 100644
--- a/aom/src/main/java/com/nedap/archie/adl14/treewalkers/Adl14CComplexObjectParser.java
+++ b/aom/src/main/java/com/nedap/archie/adl14/treewalkers/Adl14CComplexObjectParser.java
@@ -206,7 +206,7 @@ private CObject parseNonPrimitiveObject(C_non_primitive_objectContext objectCont
CTerminologyCode cCode = new CTerminologyCode();
TerminologyCode code = TerminologyCode.createFromString(ordinal_termContext.c_terminology_code().getText());
- cCode.addConstraint(code.getCodeString());
+ cCode.setConstraint(code.getCodeString());
primitiveTuple.addMember(cValue);
primitiveTuple.addMember(cCode);
@@ -275,7 +275,7 @@ private void parseCDVQuantity(C_non_primitive_objectContext objectContext, CComp
CAttribute property = new CAttribute("property");
CTerminologyCode code = new CTerminologyCode();
//will be converted later
- code.addConstraint(cdvQuantity.getProperty().toString());
+ code.setConstraint(cdvQuantity.getProperty().toString());
property.addChild(code);
result.addAttribute(property);
}
diff --git a/aom/src/main/java/com/nedap/archie/adl14/treewalkers/Adl14PrimitivesConstraintParser.java b/aom/src/main/java/com/nedap/archie/adl14/treewalkers/Adl14PrimitivesConstraintParser.java
index 2aa0ddfcb..be423ccde 100644
--- a/aom/src/main/java/com/nedap/archie/adl14/treewalkers/Adl14PrimitivesConstraintParser.java
+++ b/aom/src/main/java/com/nedap/archie/adl14/treewalkers/Adl14PrimitivesConstraintParser.java
@@ -17,6 +17,7 @@
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.TerminalNode;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -103,23 +104,29 @@ public CTerminologyCode parseCTerminologyCode(Adl14Parser.C_terminology_codeCont
//need to do parsing here because the lexer matched the entire term code ref
TerminologyCode terminologyCode = TerminologyCode.createFromString(qualifiedTermCodeContext.TERM_CODE_REF().getText());
if (terminologyCode.getTerminologyId() != null && terminologyCode.getTerminologyId().equalsIgnoreCase("local")) {
- result.addConstraint(terminologyCode.getCodeString());
+ result.setConstraint(terminologyCode.getCodeString());
} else {
//non-local term constraints. Just add the text here, it will be converted later
- result.addConstraint(qualifiedTermCodeContext.TERM_CODE_REF().getText());
+ result.setConstraint(qualifiedTermCodeContext.TERM_CODE_REF().getText());
}
} else {
String terminologyId = qualifiedTermCodeContext.identifier(0).getText();
if (terminologyId.equalsIgnoreCase("local")) {
- //we need to create a value set. For now just add the constraint, the value set will come after
- //the parser
+ List pendingCodes = new ArrayList<>();
for (int i = 1; i < qualifiedTermCodeContext.identifier().size(); i++) {
- result.addConstraint(qualifiedTermCodeContext.identifier(i).getText());
+ pendingCodes.add(qualifiedTermCodeContext.identifier(i).getText());
+ }
+ if (!pendingCodes.isEmpty()) {
+ result.setPendingCodes(pendingCodes);
}
} else {
- //non-local term constraints. Add the text here, will be converted later
- for (int i = 0; i < qualifiedTermCodeContext.identifier().size(); i++) {
- result.addConstraint(qualifiedTermCodeContext.identifier(i).getText());
+ // Normalise to full term code refs so the converter can treat all pending codes uniformly
+ List pendingCodes = new ArrayList<>();
+ for (int i = 1; i < qualifiedTermCodeContext.identifier().size(); i++) {
+ pendingCodes.add("[" + terminologyId + "::" + qualifiedTermCodeContext.identifier(i).getText() + "]");
+ }
+ if (!pendingCodes.isEmpty()) {
+ result.setPendingCodes(pendingCodes);
}
}
@@ -130,7 +137,7 @@ public CTerminologyCode parseCTerminologyCode(Adl14Parser.C_terminology_codeCont
} else {
//this is an AC-code.
if(terminologyCodeContext.localTermCode().AC_CODE() != null) {
- result.addConstraint(terminologyCodeContext.localTermCode().AC_CODE().getText());
+ result.setConstraint(terminologyCodeContext.localTermCode().AC_CODE().getText());
} else {
throw new RuntimeException("unknown terminology code format - this looks adl2 inside the adl 1.4 format?");
}
diff --git a/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/PrimitivesConstraintParser.java b/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/PrimitivesConstraintParser.java
index 534be0060..2e58b78dc 100644
--- a/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/PrimitivesConstraintParser.java
+++ b/aom/src/main/java/com/nedap/archie/adlparser/treewalkers/PrimitivesConstraintParser.java
@@ -110,12 +110,12 @@ public CTerminologyCode parseCTerminologyCode(AdlParser.C_terminology_codeContex
String assumedValueString = terminologyCodeContext.AT_CODE().getText();
assumedValue.setCodeString(assumedValueString);
result.setAssumedValue(assumedValue);
- result.addConstraint(assumedValue.getTerminologyIdString());
+ result.setConstraint(assumedValue.getTerminologyIdString());
} else {
if(terminologyCodeContext.AC_CODE() != null) {
- result.addConstraint(terminologyCodeContext.AC_CODE().getText());
+ result.setConstraint(terminologyCodeContext.AC_CODE().getText());
} else {
- result.addConstraint(terminologyCodeContext.AT_CODE().getText());
+ result.setConstraint(terminologyCodeContext.AT_CODE().getText());
}
}
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 a9d6604da..c47611ad5 100644
--- a/aom/src/main/java/com/nedap/archie/aom/Archetype.java
+++ b/aom/src/main/java/com/nedap/archie/aom/Archetype.java
@@ -205,8 +205,8 @@ public Set getAllUsedCodes() {
if(cObject instanceof CTerminologyCode) {
CTerminologyCode terminologyCode = (CTerminologyCode) cObject;
result.addAll(terminologyCode.getValueSetExpanded());
- if(!terminologyCode.getConstraint().isEmpty()) {
- result.add(terminologyCode.getConstraint().get(0));
+ if(terminologyCode.getConstraint() != null) {
+ result.add(terminologyCode.getConstraint());
}
}
for(CAttribute attribute:cObject.getAttributes()) {
diff --git a/aom/src/main/java/com/nedap/archie/aom/CPrimitiveObject.java b/aom/src/main/java/com/nedap/archie/aom/CPrimitiveObject.java
index c14458a19..31c65e886 100644
--- a/aom/src/main/java/com/nedap/archie/aom/CPrimitiveObject.java
+++ b/aom/src/main/java/com/nedap/archie/aom/CPrimitiveObject.java
@@ -1,6 +1,7 @@
package com.nedap.archie.aom;
import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.nedap.archie.aom.utils.ConformanceCheckResult;
import com.nedap.archie.archetypevalidator.ErrorType;
import com.nedap.archie.rminfo.ArchieModelNamingStrategy;
@@ -35,11 +36,10 @@ public abstract class CPrimitiveObject extends CDefinedOb
public abstract void setAssumedValue(ValueType assumedValue);
- public abstract List getConstraint();
+ public abstract Constraint getConstraint();
- public abstract void setConstraint(List constraint);
-
- public abstract void addConstraint(Constraint constraint);
+ @JsonIgnore
+ public abstract List> getConstraintAsList();
@JsonAlias("is_enumerated_type_constraint")
@RMProperty("is_enumerated_type_constraint")
@@ -74,10 +74,10 @@ public void setNodeId(String nodeId) {
*/
@Deprecated
public boolean isValidValue(ValueType value) {
- if(getConstraint().isEmpty()) {
+ if(getConstraintAsList().isEmpty()) {
return true;
}
- for(Constraint constraint:getConstraint()) {
+ for(Object constraint:getConstraintAsList()) {
if(Objects.equals(constraint, value)) {
return true;
}
@@ -105,7 +105,7 @@ public String toString() {
StringBuilder result = new StringBuilder();
result.append("{");
boolean first = true;
- for(Constraint constraint:getConstraint()) {
+ for(Object constraint:getConstraintAsList()) {
if(!first) {
result.append(", ");
}
diff --git a/aom/src/main/java/com/nedap/archie/aom/primitives/CBoolean.java b/aom/src/main/java/com/nedap/archie/aom/primitives/CBoolean.java
index ad02763d5..40f5911d0 100644
--- a/aom/src/main/java/com/nedap/archie/aom/primitives/CBoolean.java
+++ b/aom/src/main/java/com/nedap/archie/aom/primitives/CBoolean.java
@@ -20,7 +20,7 @@
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name="C_BOOLEAN")
-public class CBoolean extends CPrimitiveObject {
+public class CBoolean extends CPrimitiveObject, Boolean> {
@XmlElement(name="assumed_value")
@Nullable
private Boolean assumedValue;
@@ -43,12 +43,15 @@ public List getConstraint() {
}
@Override
+ public List getConstraintAsList() {
+ return getConstraint();
+ }
+
public void setConstraint(List constraint) {
this.constraint = constraint;
}
- @Override
public void addConstraint(Boolean constraint) {
this.constraint.add(constraint);
}
diff --git a/aom/src/main/java/com/nedap/archie/aom/primitives/COrdered.java b/aom/src/main/java/com/nedap/archie/aom/primitives/COrdered.java
index 7bd3a4f60..170d76b99 100644
--- a/aom/src/main/java/com/nedap/archie/aom/primitives/COrdered.java
+++ b/aom/src/main/java/com/nedap/archie/aom/primitives/COrdered.java
@@ -7,12 +7,23 @@
import com.nedap.archie.base.Interval;
import org.openehr.utils.message.I18n;
+import java.util.List;
import java.util.function.BiFunction;
/**
* Created by pieter.bos on 15/10/15.
*/
-public abstract class COrdered extends CPrimitiveObject, T> {
+public abstract class COrdered extends CPrimitiveObject>, T> {
+
+ public abstract List> getConstraint();
+
+ public List> getConstraintAsList() {
+ return getConstraint();
+ }
+
+ public abstract void setConstraint(List> constraint);
+
+ public abstract void addConstraint(Interval constraint);
@Override
@Deprecated
diff --git a/aom/src/main/java/com/nedap/archie/aom/primitives/CString.java b/aom/src/main/java/com/nedap/archie/aom/primitives/CString.java
index 425ca8dcd..6ae793b49 100644
--- a/aom/src/main/java/com/nedap/archie/aom/primitives/CString.java
+++ b/aom/src/main/java/com/nedap/archie/aom/primitives/CString.java
@@ -22,7 +22,7 @@
*/
@XmlType(name="C_STRING")
@XmlAccessorType(XmlAccessType.FIELD)
-public class CString extends CPrimitiveObject {
+public class CString extends CPrimitiveObject, String> {
@XmlElement(name="assumed_value")
@Nullable
@@ -53,11 +53,14 @@ public List getConstraint() {
}
@Override
+ public List getConstraintAsList() {
+ return getConstraint();
+ }
+
public void setConstraint(List constraint) {
this.constraint = constraint;
}
- @Override
public void addConstraint(String constraint) {
this.constraint.add(constraint);
}
diff --git a/aom/src/main/java/com/nedap/archie/aom/primitives/CTerminologyCode.java b/aom/src/main/java/com/nedap/archie/aom/primitives/CTerminologyCode.java
index 3063e518c..bba7040f9 100644
--- a/aom/src/main/java/com/nedap/archie/aom/primitives/CTerminologyCode.java
+++ b/aom/src/main/java/com/nedap/archie/aom/primitives/CTerminologyCode.java
@@ -18,12 +18,14 @@
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlTransient;
import jakarta.xml.bind.annotation.XmlType;
import org.openehr.utils.message.I18n;
import javax.annotation.Nullable;
import java.net.URI;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
@@ -38,11 +40,16 @@ public class CTerminologyCode extends CPrimitiveObject
@XmlElement(name="assumed_value")
@Nullable
private TerminologyCode assumedValue;
- private List constraint = new ArrayList<>();
+ private String constraint;
@Nullable
private ConstraintStatus constraintStatus;
+ /** Temporary storage for multi-code ADL 1.4 constraints during conversion. Never serialized. */
+ @JsonIgnore
+ @XmlTransient
+ private List pendingCodes;
+
@Override
public TerminologyCode getAssumedValue() {
return assumedValue;
@@ -54,18 +61,17 @@ public void setAssumedValue(TerminologyCode assumedValue) {
}
@Override
- public List getConstraint() {
+ public String getConstraint() {
return this.constraint;
}
@Override
- public void setConstraint(List constraint) {
- this.constraint = constraint;
+ public List getConstraintAsList() {
+ return getConstraint() == null ? Collections.emptyList() : Collections.singletonList(getConstraint());
}
- @Override
- public void addConstraint(String constraint) {
- this.constraint.add(constraint);
+ public void setConstraint(String constraint) {
+ this.constraint = constraint;
}
public ConstraintStatus getConstraintStatus() {
@@ -76,6 +82,16 @@ public void setConstraintStatus(ConstraintStatus constraintStatus) {
this.constraintStatus = constraintStatus;
}
+ @JsonIgnore
+ public List getPendingCodes() {
+ return pendingCodes;
+ }
+
+ @JsonIgnore
+ public void setPendingCodes(List pendingCodes) {
+ this.pendingCodes = pendingCodes;
+ }
+
@JsonIgnore
public boolean isConstraintRequired() {
return getEffectiveConstraintStatus() == ConstraintStatus.REQUIRED;
@@ -88,7 +104,7 @@ public ConstraintStatus getEffectiveConstraintStatus() {
@Override
@Deprecated
public boolean isValidValue(TerminologyCode value) {
- if(getConstraint().isEmpty()) {
+ if(getConstraint() == null) {
return true;
}
if(isConstraintRequired()) {
@@ -137,7 +153,7 @@ public List getTerms() {
ArchetypeTerminology terminology = archetype.getTerminology(this);
String language = ArchieLanguageConfiguration.getMeaningAndDescriptionLanguage();
String defaultLanguage = ArchieLanguageConfiguration.getDefaultMeaningAndDescriptionLanguage();
- for(String constraint:getConstraint()) {
+ if(constraint != null) {
if(constraint.startsWith("at")) {
ArchetypeTerm termDefinition = terminology.getTermDefinition(language, constraint);
if(termDefinition == null) {
@@ -182,7 +198,7 @@ private ArchetypeTerminology getTerminology() {
public List getValueSetExpanded() {
List result = new ArrayList<>();
ArchetypeTerminology terminology = getTerminology();
- for(String constraint:getConstraint()) {
+ if(constraint != null) {
if(constraint.startsWith("at")) {
result.add(constraint);
} else if (constraint.startsWith("ac")) {
@@ -231,11 +247,13 @@ public ConformanceCheckResult cConformsTo(CObject other, BiFunction valueSet = getValueSetExpanded();
List otherValueSet = otherCode.getValueSetExpanded();
- if(constraint.size() != 1) {
- return ConformanceCheckResult.fails(ErrorType.VPOV, I18n.t("child CTerminology code contains more than one constraint, that is not valid. Constraints are: {0}", constraint));
+ // A null constraint means unconstrained — an unconstrained parent accepts anything,
+ // and an unconstrained child trivially conforms to any parent.
+ if(otherCode.constraint == null) {
+ return ConformanceCheckResult.conforms();
}
- if(otherCode.constraint.size() != 1) {
- return ConformanceCheckResult.fails(ErrorType.VPOV, I18n.t("parent CTerminology code contains more than one constraint, that is not valid. Constraints are: {0}", constraint));
+ if(constraint == null) {
+ return ConformanceCheckResult.conforms();
}
if(!getEffectiveConstraintStatus().cConformsTo(otherCode.getEffectiveConstraintStatus()) ) {
@@ -243,8 +261,8 @@ public ConformanceCheckResult cConformsTo(CObject other, BiFunction constraint = (Constraint>) rightOperand;
- if(constraint.getItem() != null && constraint.getItem().getConstraint() != null && !constraint.getItem().getConstraint().isEmpty() &&
- constraint.getItem() instanceof CString) {
+ if(constraint.getItem() != null && constraint.getItem() instanceof CString && constraint.getItem().getConstraint() != null && !((CString) constraint.getItem()).getConstraint().isEmpty()) {
String pattern = ((CString) constraint.getItem()).getConstraint().get(0);
if (pattern.startsWith("^") || pattern.startsWith("/")) {
//regexp
diff --git a/aom/src/main/java/com/nedap/archie/serializer/adl/constraints/CTerminologyCodeSerializer.java b/aom/src/main/java/com/nedap/archie/serializer/adl/constraints/CTerminologyCodeSerializer.java
index d5e2242be..e071c0983 100644
--- a/aom/src/main/java/com/nedap/archie/serializer/adl/constraints/CTerminologyCodeSerializer.java
+++ b/aom/src/main/java/com/nedap/archie/serializer/adl/constraints/CTerminologyCodeSerializer.java
@@ -16,7 +16,7 @@ public CTerminologyCodeSerializer(ADLDefinitionSerializer serializer) {
@Override
public void serialize(CTerminologyCode cobj) {
- if (!cobj.getConstraint().isEmpty()) {
+ if (cobj.getConstraint() != null) {
if(cobj.getConstraintStatus() != null) {
String constraintStatusString = null;
switch(cobj.getConstraintStatus()) {
@@ -39,7 +39,7 @@ public void serialize(CTerminologyCode cobj) {
builder.append(" ");
}
builder.append("[");
- String constraint = cobj.getConstraint().get(0);
+ String constraint = cobj.getConstraint();
builder.append(constraint);
if (cobj.getAssumedValue() != null && cobj.getAssumedValue().getCodeString()!=null) {
builder.append("; ").append(cobj.getAssumedValue().getCodeString());
@@ -49,8 +49,8 @@ public void serialize(CTerminologyCode cobj) {
}
public String getSimpleCommentText(CTerminologyCode cobj) {
- if (!cobj.getConstraint().isEmpty()) {
- String constraint = cobj.getConstraint().get(0);
+ if (cobj.getConstraint() != null) {
+ String constraint = cobj.getConstraint();
if(AOMUtils.isValueSetCode(constraint) || AOMUtils.isValueCode(constraint)) {
return serializer.getTermText(cobj, constraint);
}
diff --git a/openehr-rm/src/main/java/com/nedap/archie/rminfo/UpdatedValueHandler.java b/openehr-rm/src/main/java/com/nedap/archie/rminfo/UpdatedValueHandler.java
index 3ea7d117c..75b3377ef 100644
--- a/openehr-rm/src/main/java/com/nedap/archie/rminfo/UpdatedValueHandler.java
+++ b/openehr-rm/src/main/java/com/nedap/archie/rminfo/UpdatedValueHandler.java
@@ -69,8 +69,8 @@ private static Map fixDvOrdinalOrDvScale(Object rmObject, Archet
int symbolIndex = socParent.getMemberIndex("symbol");
if (valueIndex != -1 && symbolIndex != -1) {
for (CPrimitiveTuple tuple : socParent.getTuples()) {
- if ((ordered instanceof DvOrdinal && tuple.getMembers().get(symbolIndex).getConstraint().get(0).equals(((DvOrdinal) ordered).getSymbol().getDefiningCode().getCodeString())) ||
- ordered instanceof DvScale && tuple.getMembers().get(symbolIndex).getConstraint().get(0).equals(((DvScale) ordered).getSymbol().getDefiningCode().getCodeString())) {
+ if ((ordered instanceof DvOrdinal && tuple.getMembers().get(symbolIndex).getConstraint().equals(((DvOrdinal) ordered).getSymbol().getDefiningCode().getCodeString())) ||
+ ordered instanceof DvScale && tuple.getMembers().get(symbolIndex).getConstraint().equals(((DvScale) ordered).getSymbol().getDefiningCode().getCodeString())) {
List> valueConstraint = (List>) tuple.getMembers().get(valueIndex).getConstraint();
if(valueConstraint.size() == 1) {
Interval interval = valueConstraint.get(0);
diff --git a/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/CodeValidation.java b/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/CodeValidation.java
index da52929ab..05e734771 100644
--- a/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/CodeValidation.java
+++ b/tools/src/main/java/com/nedap/archie/archetypevalidator/validations/CodeValidation.java
@@ -42,7 +42,8 @@ public void validate(CTerminologyCode cTerminologyCode) {
//validate CTerminology codes
int archetypeSpecializationDepth = archetype.specializationDepth();
- for(String constraint:cTerminologyCode.getConstraint()) {
+ String constraint = cTerminologyCode.getConstraint();
+ if(constraint != null) {
if(AOMUtils.isValueSetCode(constraint)) {
int codeSpecializationDepth = AOMUtils.getSpecializationDepthFromCode(constraint);
if(codeSpecializationDepth > archetypeSpecializationDepth) {
diff --git a/tools/src/main/java/com/nedap/archie/creation/ExampleJsonInstanceGenerator.java b/tools/src/main/java/com/nedap/archie/creation/ExampleJsonInstanceGenerator.java
index d61053c5a..de6d2e71a 100644
--- a/tools/src/main/java/com/nedap/archie/creation/ExampleJsonInstanceGenerator.java
+++ b/tools/src/main/java/com/nedap/archie/creation/ExampleJsonInstanceGenerator.java
@@ -447,7 +447,7 @@ protected Object generateTerminologyCode(CTerminologyCode child) {
terminologyId.put("value", "local");
String termString = "term";
ArchetypeTerminology terminology = archetype.getTerminology(child);
- if(child.getConstraint().isEmpty()) {
+ if(child.getConstraint() == null) {
codeString = "term code";
CAttribute attribute = child.getParent();
CComplexObject parent = (CComplexObject) attribute.getParent();
@@ -456,7 +456,7 @@ protected Object generateTerminologyCode(CTerminologyCode child) {
return potentialResult;
}
} else {
- String constraint = child.getConstraint().get(0);
+ String constraint = child.getConstraint();
if(constraint.startsWith("ac")) {
ValueSet valueSet = terminology.getValueSets().get(constraint);
diff --git a/tools/src/main/java/com/nedap/archie/diff/UnconstrainedIntervalRemover.java b/tools/src/main/java/com/nedap/archie/diff/UnconstrainedIntervalRemover.java
index 0052ddd70..ec8fa25ea 100644
--- a/tools/src/main/java/com/nedap/archie/diff/UnconstrainedIntervalRemover.java
+++ b/tools/src/main/java/com/nedap/archie/diff/UnconstrainedIntervalRemover.java
@@ -1,6 +1,7 @@
package com.nedap.archie.diff;
import com.nedap.archie.aom.*;
+import com.nedap.archie.aom.primitives.COrdered;
import com.nedap.archie.base.Interval;
import java.util.ArrayList;
@@ -29,9 +30,11 @@ public static void removeUnconstrainedIntervals(CAttribute cAttribute) {
for(CObject cObject:cAttribute.getChildren()) {
if(cObject instanceof CComplexObject) {
removeUnconstrainedIntervals((CComplexObject) cObject);
- } else if (cObject instanceof CPrimitiveObject) {
- CPrimitiveObject, ?> cPrimitiveObject = (CPrimitiveObject, ?>) cObject;
- List> constraint = cPrimitiveObject.getConstraint();
+ } else if (cObject instanceof COrdered) {
+ // COrdered is used explicitly rather than CPrimitiveObject + getConstraintAsList(),
+ // because the list is mutated in-place and getConstraintAsList() does not guarantee a mutable list.
+ COrdered> cOrdered = (COrdered>) cObject;
+ List> constraint = cOrdered.getConstraint();
List