Allow runtime query of bound enum type

This commit is contained in:
James Agnew 2016-03-18 19:41:43 +01:00
parent 82c6d82444
commit ab4deb406c
7 changed files with 126 additions and 22 deletions

View File

@ -47,14 +47,18 @@ public abstract class BaseRuntimeChildDatatypeDefinition extends BaseRuntimeDecl
myDatatype = theDatatype;
}
/**
* If this child has a bound type, this method will return the Enum type that
* it is bound to. Otherwise, will return <code>null</code>.
*/
public Class<? extends Enum<?>> getBoundEnumType() {
return null;
}
@Override
public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
Class<?> nextType = theDatatype;
while (nextType.equals(Object.class) == false) {
if (myDatatype.equals(nextType)) {
return getElementName();
}
nextType = nextType.getSuperclass();
public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
if (getElementName().equals(theName)) {
return myElementDefinition;
}
return null;
}
@ -72,9 +76,13 @@ public abstract class BaseRuntimeChildDatatypeDefinition extends BaseRuntimeDecl
}
@Override
public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
if (getElementName().equals(theName)) {
return myElementDefinition;
public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
Class<?> nextType = theDatatype;
while (nextType.equals(Object.class) == false) {
if (myDatatype.equals(nextType)) {
return getElementName();
}
nextType = nextType.getSuperclass();
}
return null;
}

View File

@ -576,9 +576,10 @@ class ModelScanner {
if (IPrimitiveType.class.isAssignableFrom(nextElementType)) {
if (nextElementType.equals(BoundCodeDt.class)) {
IValueSetEnumBinder<Enum<?>> binder = getBoundCodeBinder(next);
def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder);
Class<? extends Enum<?>> enumType = determineEnumTypeForBoundField(next);
def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
} else if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
Class<?> binderType = ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next);
Class<? extends Enum<?>> binderType = determineEnumTypeForBoundField(next);
def = new RuntimeChildPrimitiveEnumerationDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binderType);
} else {
def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
@ -588,7 +589,8 @@ class ModelScanner {
} else {
if (IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
IValueSetEnumBinder<Enum<?>> binder = getBoundCodeBinder(next);
def = new RuntimeChildCompositeBoundDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder);
Class<? extends Enum<?>> enumType = determineEnumTypeForBoundField(next);
def = new RuntimeChildCompositeBoundDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
} else if (BaseNarrativeDt.class.isAssignableFrom(nextElementType) || INarrative.class.isAssignableFrom(nextElementType)) {
def = new RuntimeChildNarrativeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
} else {
@ -618,6 +620,12 @@ class ModelScanner {
}
}
private Class<? extends Enum<?>> determineEnumTypeForBoundField(Field next) {
@SuppressWarnings("unchecked")
Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next);
return enumType;
}
private String scanPrimitiveDatatype(Class<? extends IPrimitiveType<?>> theClass, DatatypeDef theDatatypeDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.context;
import java.lang.reflect.Field;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
@ -31,13 +32,15 @@ import ca.uhn.fhir.model.api.annotation.Description;
public class RuntimeChildCompositeBoundDatatypeDefinition extends RuntimeChildCompositeDatatypeDefinition {
private IValueSetEnumBinder<Enum<?>> myBinder;
private Class<? extends Enum<?>> myEnumType;
public RuntimeChildCompositeBoundDatatypeDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, Class<? extends IBase> theDatatype, IValueSetEnumBinder<Enum<?>> theBinder) {
public RuntimeChildCompositeBoundDatatypeDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, Class<? extends IBase> theDatatype, IValueSetEnumBinder<Enum<?>> theBinder, Class<? extends Enum<?>> theEnumType) {
super(theField, theElementName, theChildAnnotation, theDescriptionAnnotation, theDatatype);
Validate.notNull(theBinder, "theBinder must not be null");
Validate.notNull(theEnumType, "theEnumType must not be null");
myBinder = theBinder;
if (theBinder==null) {
throw new IllegalArgumentException("Binder must not be null");
}
myEnumType = theEnumType;
}
@Override
@ -45,4 +48,9 @@ public class RuntimeChildCompositeBoundDatatypeDefinition extends RuntimeChildCo
return myBinder;
}
@Override
public Class<? extends Enum<?>> getBoundEnumType() {
return myEnumType;
}
}

View File

@ -30,11 +30,18 @@ import ca.uhn.fhir.model.api.annotation.Description;
public class RuntimeChildPrimitiveBoundCodeDatatypeDefinition extends RuntimeChildPrimitiveDatatypeDefinition {
private Object myBinder;
private Class<? extends Enum<?>> myEnumType;
public RuntimeChildPrimitiveBoundCodeDatatypeDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, Class<? extends IBase> theDatatype, Object theBinder) {
public RuntimeChildPrimitiveBoundCodeDatatypeDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, Class<? extends IBase> theDatatype, Object theBinder, Class<? extends Enum<?>> theEnumType) {
super(theField, theElementName, theDescriptionAnnotation, theChildAnnotation, theDatatype);
myBinder = theBinder;
myEnumType = theEnumType;
}
@Override
public Class<? extends Enum<?>> getBoundEnumType() {
return myEnumType;
}
@Override

View File

@ -30,19 +30,24 @@ import ca.uhn.fhir.model.api.annotation.Description;
public class RuntimeChildPrimitiveEnumerationDatatypeDefinition extends RuntimeChildPrimitiveDatatypeDefinition {
private Object myBinder;
private Class<?> myEnumerationType;
private Class<? extends Enum<?>> myEnumType;
public RuntimeChildPrimitiveEnumerationDatatypeDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, Class<? extends IBase> theDatatype, Class<?> theBinderType) {
public RuntimeChildPrimitiveEnumerationDatatypeDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, Class<? extends IBase> theDatatype, Class<? extends Enum<?>> theBinderType) {
super(theField, theElementName, theDescriptionAnnotation, theChildAnnotation, theDatatype);
myEnumerationType = theBinderType;
myEnumType = theBinderType;
}
@Override
public Class<? extends Enum<?>> getBoundEnumType() {
return myEnumType;
}
@Override
public Object getInstanceConstructorArguments() {
Object retVal = myBinder;
if (retVal == null) {
retVal = toEnumFactory(myEnumerationType);
retVal = toEnumFactory(myEnumType);
myBinder = retVal;
}
return retVal;

View File

@ -0,0 +1,39 @@
package ca.uhn.fhir.ctx;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.MaritalStatusCodesEnum;
public class FhirContextDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContextDstu2Test.class);
private FhirContext ourCtx = FhirContext.forDstu2();
@Test
public void testQueryBoundCode() {
RuntimeResourceDefinition patientType = ourCtx.getResourceDefinition(Patient.class);
String childName = "gender";
BaseRuntimeChildDatatypeDefinition genderChild = (BaseRuntimeChildDatatypeDefinition) patientType.getChildByName(childName);
ourLog.trace(genderChild.getClass().getName());
assertEquals(AdministrativeGenderEnum.class, genderChild.getBoundEnumType());
}
@Test
public void testQueryBoundCodeableConcept() {
RuntimeResourceDefinition patientType = ourCtx.getResourceDefinition(Patient.class);
String childName = "maritalStatus";
BaseRuntimeChildDatatypeDefinition genderChild = (BaseRuntimeChildDatatypeDefinition) patientType.getChildByName(childName);
ourLog.trace(genderChild.getClass().getName());
assertEquals(MaritalStatusCodesEnum.class, genderChild.getBoundEnumType());
}
}

View File

@ -0,0 +1,29 @@
package org.hl7.fhir.dstu3.hapi.ctx;
import static org.junit.Assert.assertEquals;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.Test;
import ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
public class FhirContextDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContextDstu3Test.class);
private FhirContext ourCtx = FhirContext.forDstu3();
@Test
public void testQueryBoundCode() {
RuntimeResourceDefinition patientType = ourCtx.getResourceDefinition(Patient.class);
String childName = "gender";
BaseRuntimeChildDatatypeDefinition genderChild = (BaseRuntimeChildDatatypeDefinition) patientType.getChildByName(childName);
ourLog.trace(genderChild.getClass().getName());
assertEquals(AdministrativeGender.class, genderChild.getBoundEnumType());
}
}