From e8240853300241c27e0f7debd398383f36e70f83 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Thu, 22 Feb 2024 11:50:52 -0500 Subject: [PATCH] WIP on choice specialization --- .../ca/uhn/fhir/context/RuntimeChildAny.java | 50 +++++++++------ .../context/RuntimeChildChoiceDefinition.java | 63 ++++++++++++------- ...ntimeChildDeclaredExtensionDefinition.java | 2 +- .../validator/ValidatorWrapper.java | 2 +- 4 files changed, 71 insertions(+), 46 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildAny.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildAny.java index b8bfba56910..a8121749707 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildAny.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildAny.java @@ -46,13 +46,15 @@ public class RuntimeChildAny extends RuntimeChildChoiceDefinition { void sealAndInitialize( FhirContext theContext, Map, BaseRuntimeElementDefinition> theClassToElementDefinitions) { - List> choiceTypes = new ArrayList>(); + List> choiceTypes = new ArrayList<>(); + List> spacializationChoiceTypes = new ArrayList<>(); for (Class next : theClassToElementDefinitions.keySet()) { if (next.equals(XhtmlDt.class)) { continue; } + boolean isSpecialization = false; BaseRuntimeElementDefinition nextDef = theClassToElementDefinitions.get(next); if (nextDef instanceof IRuntimeDatatypeDefinition) { if (((IRuntimeDatatypeDefinition) nextDef).isSpecialization()) { @@ -60,36 +62,44 @@ public class RuntimeChildAny extends RuntimeChildChoiceDefinition { * Things like BoundCodeDt shoudn't be considered as valid options for an "any" choice, since * we'll already have CodeDt as an option */ - continue; - } + isSpecialization = true; + } } if (IResource.class.isAssignableFrom(next) || IDatatype.class.isAssignableFrom(next) || IBaseDatatype.class.isAssignableFrom(next) || IBaseReference.class.isAssignableFrom(next)) { - choiceTypes.add(next); - } - } - Collections.sort(choiceTypes, new Comparator>() { - @Override - public int compare(Class theO1, Class theO2) { - boolean o1res = IResource.class.isAssignableFrom(theO1); - boolean o2res = IResource.class.isAssignableFrom(theO2); - if (o1res && o2res) { - return theO1.getSimpleName().compareTo(theO2.getSimpleName()); - } else if (o1res) { - return -1; - } else if (o1res == false && o2res == false) { - return 0; + if (isSpecialization) { + spacializationChoiceTypes.add(next); } else { - return 1; + choiceTypes.add(next); } } - }); + } - setChoiceTypes(choiceTypes); + choiceTypes.sort(new ResourceTypeNameComparator()); + spacializationChoiceTypes.sort(new ResourceTypeNameComparator()); + + setChoiceTypes(choiceTypes, spacializationChoiceTypes); super.sealAndInitialize(theContext, theClassToElementDefinitions); } + + private static class ResourceTypeNameComparator implements Comparator> { + @Override + public int compare(Class theO1, Class theO2) { + boolean o1res = IResource.class.isAssignableFrom(theO1); + boolean o2res = IResource.class.isAssignableFrom(theO2); + if (o1res && o2res) { + return theO1.getSimpleName().compareTo(theO2.getSimpleName()); + } else if (o1res) { + return -1; + } else if (o1res == false && o2res == false) { + return 0; + } else { + return 1; + } + } + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildChoiceDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildChoiceDefinition.java index f251a2bb878..2def4d8b2fc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildChoiceDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildChoiceDefinition.java @@ -22,12 +22,14 @@ package ca.uhn.fhir.context; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Description; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.thymeleaf.util.Validate; import java.lang.reflect.Field; import java.util.ArrayList; @@ -45,6 +47,7 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini private Map, BaseRuntimeElementDefinition> myDatatypeToElementDefinition; private String myReferenceSuffix; private List> myResourceTypes; + private List> mySpecializationChoiceTypes = List.of(); /** * Constructor @@ -70,8 +73,11 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini super(theField, theChildAnnotation, theDescriptionAnnotation, theElementName); } - void setChoiceTypes(List> theChoiceTypes) { + void setChoiceTypes(@Nonnull List> theChoiceTypes, @Nonnull List> theSpecializationChoiceTypes) { + Validate.notNull(theChoiceTypes, "theChoiceTypes must not be null"); + Validate.notNull(theSpecializationChoiceTypes, "theSpecializationChoiceTypes must not be null"); myChoiceTypes = Collections.unmodifiableList(theChoiceTypes); + mySpecializationChoiceTypes = Collections.unmodifiableList(theSpecializationChoiceTypes); } public List> getChoices() { @@ -96,14 +102,24 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini void sealAndInitialize( FhirContext theContext, Map, BaseRuntimeElementDefinition> theClassToElementDefinitions) { - myNameToChildDefinition = new HashMap>(); - myDatatypeToElementName = new HashMap, String>(); - myDatatypeToElementDefinition = new HashMap, BaseRuntimeElementDefinition>(); - myResourceTypes = new ArrayList>(); + myNameToChildDefinition = new HashMap<>(); + myDatatypeToElementName = new HashMap<>(); + myDatatypeToElementDefinition = new HashMap<>(); + myResourceTypes = new ArrayList<>(); myReferenceSuffix = "Reference"; - for (Class next : myChoiceTypes) { + sealAndInitializeChoiceTypes(theContext, theClassToElementDefinitions, mySpecializationChoiceTypes, true); + sealAndInitializeChoiceTypes(theContext, theClassToElementDefinitions, myChoiceTypes, false); + + myNameToChildDefinition = Collections.unmodifiableMap(myNameToChildDefinition); + myDatatypeToElementName = Collections.unmodifiableMap(myDatatypeToElementName); + myDatatypeToElementDefinition = Collections.unmodifiableMap(myDatatypeToElementDefinition); + myResourceTypes = Collections.unmodifiableList(myResourceTypes); + } + + private void sealAndInitializeChoiceTypes(FhirContext theContext, Map, BaseRuntimeElementDefinition> theClassToElementDefinitions, List> choiceTypes, boolean theIsSpecilization) { + for (Class next : choiceTypes) { String elementName = null; BaseRuntimeElementDefinition nextDef; @@ -112,8 +128,10 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini elementName = getElementName() + StringUtils.capitalize(next.getSimpleName()); nextDef = findResourceReferenceDefinition(theClassToElementDefinitions); - myNameToChildDefinition.put(getElementName() + "Reference", nextDef); - myNameToChildDefinition.put(getElementName() + "Resource", nextDef); + if (!theIsSpecilization) { + myNameToChildDefinition.put(getElementName() + "Reference", nextDef); + myNameToChildDefinition.put(getElementName() + "Resource", nextDef); + } myResourceTypes.add((Class) next); @@ -147,21 +165,23 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini } // I don't see how elementName could be null here, but eclipse complains.. - if (elementName != null) { - if (myNameToChildDefinition.containsKey(elementName) == false || !nonPreferred) { + if (!theIsSpecilization) { + if (elementName != null) { + if (myNameToChildDefinition.containsKey(elementName) == false || !nonPreferred) { + myNameToChildDefinition.put(elementName, nextDef); + } + } + + /* + * If this is a resource reference, the element name is "fooNameReference" + */ + if (IBaseResource.class.isAssignableFrom(next) || IBaseReference.class.isAssignableFrom(next)) { + next = theContext.getVersion().getResourceReferenceType(); + elementName = getElementName() + myReferenceSuffix; myNameToChildDefinition.put(elementName, nextDef); } } - /* - * If this is a resource reference, the element name is "fooNameReference" - */ - if (IBaseResource.class.isAssignableFrom(next) || IBaseReference.class.isAssignableFrom(next)) { - next = theContext.getVersion().getResourceReferenceType(); - elementName = getElementName() + myReferenceSuffix; - myNameToChildDefinition.put(elementName, nextDef); - } - myDatatypeToElementDefinition.put(next, nextDef); if (myDatatypeToElementName.containsKey(next)) { @@ -175,11 +195,6 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini myDatatypeToElementName.put(next, elementName); } } - - myNameToChildDefinition = Collections.unmodifiableMap(myNameToChildDefinition); - myDatatypeToElementName = Collections.unmodifiableMap(myDatatypeToElementName); - myDatatypeToElementDefinition = Collections.unmodifiableMap(myDatatypeToElementDefinition); - myResourceTypes = Collections.unmodifiableList(myResourceTypes); } public List> getResourceTypes() { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java index 09b8394c43c..98d592a09e3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java @@ -82,7 +82,7 @@ public class RuntimeChildDeclaredExtensionDefinition extends RuntimeChildChoiceD choiceTypes.add(theChildType); } - setChoiceTypes(choiceTypes); + setChoiceTypes(choiceTypes, List.of()); } @Override diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java index f8e0c9bda89..c946e2157a7 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java @@ -120,7 +120,7 @@ class ValidatorWrapper { try { v = new InstanceValidator(theWorkerContext, evaluationCtx, xverManager); } catch (Exception e) { - throw new ConfigurationException(Msg.code(648) + e); + throw new ConfigurationException(Msg.code(648) + e.getMessage(), e); } v.setAssumeValidRestReferences(isAssumeValidRestReferences());