Fix #423 - Encode custom types with custom type fields correctly

This commit is contained in:
James 2016-08-14 13:05:16 -04:00
parent 779b66c5e4
commit b0d19c3a65
21 changed files with 926 additions and 219 deletions

View File

@ -1,29 +0,0 @@
package ca.uhn.fhir.context;
import static org.junit.Assert.*;
import org.junit.Test;
public class MultiVersionModelScannerTest {
@Test
public void testScan() {
FhirContext ctx = FhirContext.forDstu1();
RuntimeResourceDefinition def;
def = ctx.getResourceDefinition(ca.uhn.fhir.model.dstu2.resource.Patient.class);
assertEquals(FhirVersionEnum.DSTU2, def.getStructureVersion());
def = ctx.getResourceDefinition(new ca.uhn.fhir.model.dstu2.resource.Patient());
assertEquals(FhirVersionEnum.DSTU2, def.getStructureVersion());
def = ctx.getResourceDefinition("Patient");
assertEquals(FhirVersionEnum.DSTU1, def.getStructureVersion());
assertEquals(ca.uhn.fhir.model.dstu.resource.Patient.class, def.getImplementingClass());
def = ctx.getResourceDefinition(FhirVersionEnum.DSTU2, "Patient");
assertEquals(FhirVersionEnum.DSTU2, def.getStructureVersion());
assertEquals(ca.uhn.fhir.model.dstu2.resource.Patient.class, def.getImplementingClass());
}
}

View File

@ -8,6 +8,7 @@ import org.hamcrest.Matchers;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.resource.Organization;
@ -64,14 +65,14 @@ public class MultiVersionXmlParserTest {
try {
ourCtxDstu1.newXmlParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Patient.class, res);
fail();
} catch (IllegalArgumentException e) {
assertEquals("This parser is for FHIR version DSTU1 - Can not parse a structure for version DSTU2", e.getMessage());
} catch (ConfigurationException e) {
assertEquals("This context is for FHIR version \"DSTU1\" but the class \"ca.uhn.fhir.model.dstu2.resource.Patient\" is for version \"DSTU2\"", e.getMessage());
}
try {
ourCtxDstu2.newXmlParser().parseResource(ca.uhn.fhir.model.dstu.resource.Patient.class, res);
fail();
} catch (IllegalArgumentException e) {
assertEquals("This parser is for FHIR version DSTU2 - Can not parse a structure for version DSTU1", e.getMessage());
} catch (ConfigurationException e) {
assertEquals("This context is for FHIR version \"DSTU2\" but the class \"ca.uhn.fhir.model.dstu.resource.Patient\" is for version \"DSTU1\"", e.getMessage());
}
}

View File

@ -10,27 +10,12 @@ import ca.uhn.fhir.model.dstu.resource.Patient;
public class FhirContextTest {
@Test
public void testWrongVersionDoesntGetInContext1() {
@Test
public void testWrongVersionDoesntGetInContext1() {
FhirContext ctx = FhirContext.forDstu1();
RuntimeResourceDefinition def = ctx.getResourceDefinition("Patient");
assertEquals(Patient.class, def.getImplementingClass());
}
@Test
public void testWrongVersionDoesntGetInContext2() {
FhirContext ctx = FhirContext.forDstu1();
RuntimeResourceDefinition def = ctx.getResourceDefinition("Patient");
assertEquals(Patient.class, def.getImplementingClass());
ctx = FhirContext.forDstu1();
def = ctx.getResourceDefinition(ca.uhn.fhir.model.dstu2.resource.Patient.class);
assertEquals(ca.uhn.fhir.model.dstu2.resource.Patient.class, def.getImplementingClass());
def = ctx.getResourceDefinition("Patient");
assertEquals(Patient.class, def.getImplementingClass());
}
FhirContext ctx = FhirContext.forDstu1();
RuntimeResourceDefinition def = ctx.getResourceDefinition("Patient");
assertEquals(Patient.class, def.getImplementingClass());
}
}

View File

@ -72,19 +72,59 @@ import ca.uhn.fhir.util.ReflectionUtil;
public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> extends BaseRuntimeElementDefinition<T> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseRuntimeElementCompositeDefinition.class);
private Map<String, Integer> forcedOrder = null;
private List<BaseRuntimeChildDefinition> myChildren = new ArrayList<BaseRuntimeChildDefinition>();
private List<BaseRuntimeChildDefinition> myChildrenAndExtensions;
private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions;
private FhirContext myContext;
private Map<String, BaseRuntimeChildDefinition> myNameToChild = new HashMap<String, BaseRuntimeChildDefinition>();
private List<ScannedField> myScannedFields = new ArrayList<BaseRuntimeElementCompositeDefinition.ScannedField>();
private volatile boolean mySealed;
@SuppressWarnings("unchecked")
public BaseRuntimeElementCompositeDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType, FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
super(theName, theImplementingClass, theStandardType);
myContext = theContext;
myClassToElementDefinitions = theClassToElementDefinitions;
/*
* We scan classes for annotated fields in the class but also all of its superclasses
*/
Class<? extends IBase> current = theImplementingClass;
LinkedList<Class<? extends IBase>> classes = new LinkedList<Class<? extends IBase>>();
do {
if (forcedOrder == null) {
ChildOrder childOrder = current.getAnnotation(ChildOrder.class);
if (childOrder != null) {
forcedOrder = new HashMap<String, Integer>();
for (int i = 0; i < childOrder.names().length; i++) {
forcedOrder.put(childOrder.names()[i], i);
}
}
}
classes .push(current);
if (IBase.class.isAssignableFrom(current.getSuperclass())) {
current = (Class<? extends IBase>) current.getSuperclass();
} else {
current = null;
}
} while (current != null);
Set<Field> fields = new HashSet<Field>();
for (Class<? extends IBase> nextClass : classes) {
int fieldIndexInClass = 0;
for (Field next : nextClass.getDeclaredFields()) {
if (fields.add(next)) {
ScannedField scannedField = new ScannedField(next, theImplementingClass, fieldIndexInClass == 0);
if (scannedField.getChildAnnotation() != null) {
myScannedFields.add(scannedField);
fieldIndexInClass++;
}
}
}
}
}
void addChild(BaseRuntimeChildDefinition theNext) {
@ -116,13 +156,14 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
validateSealed();
return myChildren;
}
public List<BaseRuntimeChildDefinition> getChildrenAndExtension() {
validateSealed();
return myChildrenAndExtensions;
}
/**
* Has this class been sealed
*/
@ -130,41 +171,28 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
return mySealed;
}
@SuppressWarnings("unchecked")
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, BaseRuntimeElementCompositeDefinition<?> theDefinition) {
void populateScanAlso(Set<Class<? extends IBase>> theScanAlso) {
for (ScannedField next : myScannedFields) {
if (IBase.class.isAssignableFrom(next.getElementType())) {
if (next.getElementType().isInterface() == false && Modifier.isAbstract(next.getElementType().getModifiers()) == false) {
theScanAlso.add((Class<? extends IBase>) next.getElementType());
}
}
for (Class<? extends IBase> nextChildType : next.getChoiceTypes()) {
if (nextChildType.isInterface() == false && Modifier.isAbstract(nextChildType.getModifiers()) == false) {
theScanAlso.add(nextChildType);
}
}
}
}
private void scanCompositeElementForChildren() {
Set<String> elementNames = new HashSet<String>();
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToElementDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToExtensionDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
LinkedList<Class<? extends IBase>> classes = new LinkedList<Class<? extends IBase>>();
/*
* We scan classes for annotated fields in the class but also all of its superclasses
*/
Class<? extends IBase> current = theClass;
Map<String, Integer> forcedOrder = null;
do {
if (forcedOrder == null) {
ChildOrder childOrder = current.getAnnotation(ChildOrder.class);
if (childOrder != null) {
forcedOrder = new HashMap<String, Integer>();
for (int i = 0; i < childOrder.names().length; i++) {
forcedOrder.put(childOrder.names()[i], i);
}
}
}
classes.push(current);
if (IBase.class.isAssignableFrom(current.getSuperclass())) {
current = (Class<? extends IBase>) current.getSuperclass();
} else {
current = null;
}
} while (current != null);
for (Class<? extends IBase> next : classes) {
scanCompositeElementForChildren(next, elementNames, orderToElementDef, orderToExtensionDef);
}
scanCompositeElementForChildren(elementNames, orderToElementDef, orderToExtensionDef);
if (forcedOrder != null) {
/*
@ -207,42 +235,38 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
for (Integer i : orders) {
BaseRuntimeChildDefinition nextChild = orderToElementDef.get(i);
if (nextChild != null) {
theDefinition.addChild(nextChild);
this.addChild(nextChild);
}
BaseRuntimeDeclaredChildDefinition nextExt = orderToExtensionDef.get(i);
if (nextExt != null) {
theDefinition.addExtension((RuntimeChildDeclaredExtensionDefinition) nextExt);
this.addExtension((RuntimeChildDeclaredExtensionDefinition) nextExt);
}
}
}
@SuppressWarnings("unchecked")
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
private void scanCompositeElementForChildren(Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
int baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
int baseElementOrder = 0;
for (Field next : theClass.getDeclaredFields()) {
if (Modifier.isFinal(next.getModifiers())) {
ourLog.trace("Ignoring constant {} on target type {}", next.getName(), theClass);
continue;
for (ScannedField next : myScannedFields) {
if (next.isFirstFieldInNewClass()) {
baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
}
Class<?> declaringClass = next.getField().getDeclaringClass();
Child childAnnotation = ModelScanner.pullAnnotation(next, Child.class);
if (childAnnotation == null) {
ourLog.trace("Ignoring non @Child field {} on target type {}", next.getName(), theClass);
continue;
}
Description descriptionAnnotation = ModelScanner.pullAnnotation(next, Description.class);
Description descriptionAnnotation = ModelScanner.pullAnnotation(next.getField(), Description.class);
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
Extension extensionAttr = ModelScanner.pullAnnotation(next, Extension.class);
Extension extensionAttr = ModelScanner.pullAnnotation(next.getField(), Extension.class);
if (extensionAttr != null) {
orderMap = theOrderToExtensionDef;
}
Child childAnnotation = next.getChildAnnotation();
Field nextField = next.getField();
String elementName = childAnnotation.name();
int order = childAnnotation.order();
boolean childIsChoiceType = false;
@ -265,8 +289,8 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
}
}
if (order == Child.REPLACE_PARENT) {
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
+ ") but no parent element with extension URL " + extensionAttr.url() + " could be found on type " + next.getDeclaringClass().getSimpleName());
throw new ConfigurationException("Field " + nextField.getName() + "' on target type " + declaringClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
+ ") but no parent element with extension URL " + extensionAttr.url() + " could be found on type " + nextField.getDeclaringClass().getSimpleName());
}
} else {
@ -291,8 +315,8 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
}
}
if (order == Child.REPLACE_PARENT) {
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
+ ") but no parent element with name " + elementName + " could be found on type " + next.getDeclaringClass().getSimpleName());
throw new ConfigurationException("Field " + nextField.getName() + "' on target type " + declaringClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
+ ") but no parent element with name " + elementName + " could be found on type " + nextField.getDeclaringClass().getSimpleName());
}
}
@ -300,7 +324,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
}
if (order < 0 && order != Child.ORDER_UNKNOWN) {
throw new ConfigurationException("Invalid order '" + order + "' on @Child for field '" + next.getName() + "' on target type: " + theClass);
throw new ConfigurationException("Invalid order '" + order + "' on @Child for field '" + nextField.getName() + "' on target type: " + declaringClass);
}
if (order != Child.ORDER_UNKNOWN && !orderIsReplaceParent) {
@ -319,40 +343,37 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
}
}
List<Class<? extends IBase>> choiceTypes = new ArrayList<Class<? extends IBase>>();
for (Class<? extends IBase> nextChoiceType : childAnnotation.type()) {
choiceTypes.add(nextChoiceType);
}
List<Class<? extends IBase>> choiceTypes = next.getChoiceTypes();
if (orderMap.containsKey(order)) {
throw new ConfigurationException("Detected duplicate field order '" + childAnnotation.order() + "' for element named '" + elementName + "' in type '" + theClass.getCanonicalName() + "' - Already had: " + orderMap.get(order).getElementName());
throw new ConfigurationException("Detected duplicate field order '" + childAnnotation.order() + "' for element named '" + elementName + "' in type '" + declaringClass.getCanonicalName() + "' - Already had: " + orderMap.get(order).getElementName());
}
if (elementNames.contains(elementName)) {
throw new ConfigurationException("Detected duplicate field name '" + elementName + "' in type '" + theClass.getCanonicalName() + "'");
throw new ConfigurationException("Detected duplicate field name '" + elementName + "' in type '" + declaringClass.getCanonicalName() + "'");
}
Class<?> nextElementType = ModelScanner.determineElementType(next);
Class<?> nextElementType = next.getElementType();
BaseRuntimeDeclaredChildDefinition def;
if (childAnnotation.name().equals("extension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
def = new RuntimeChildExtension(next, childAnnotation.name(), childAnnotation, descriptionAnnotation);
def = new RuntimeChildExtension(nextField, childAnnotation.name(), childAnnotation, descriptionAnnotation);
} else if (childAnnotation.name().equals("modifierExtension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
def = new RuntimeChildExtension(next, childAnnotation.name(), childAnnotation, descriptionAnnotation);
def = new RuntimeChildExtension(nextField, childAnnotation.name(), childAnnotation, descriptionAnnotation);
} else if (BaseContainedDt.class.isAssignableFrom(nextElementType) || (childAnnotation.name().equals("contained") && IBaseResource.class.isAssignableFrom(nextElementType))) {
/*
* Child is contained resources
*/
def = new RuntimeChildContainedResources(next, childAnnotation, descriptionAnnotation, elementName);
def = new RuntimeChildContainedResources(nextField, childAnnotation, descriptionAnnotation, elementName);
} else if (IAnyResource.class.isAssignableFrom(nextElementType) || IResource.class.equals(nextElementType)) {
/*
* Child is a resource as a direct child, as in Bundle.entry.resource
*/
def = new RuntimeChildDirectResource(next, childAnnotation, descriptionAnnotation, elementName);
def = new RuntimeChildDirectResource(nextField, childAnnotation, descriptionAnnotation, elementName);
} else {
childIsChoiceType |= choiceTypes.size() > 1;
if (childIsChoiceType && !BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) && !IBaseReference.class.isAssignableFrom(nextElementType)) {
def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
def = new RuntimeChildChoiceDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
} else if (extensionAttr != null) {
/*
* Child is an extension
@ -361,13 +382,13 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
Object binder = null;
if (BoundCodeDt.class.isAssignableFrom(nextElementType) || IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
binder = ModelScanner.getBoundCodeBinder(next);
binder = ModelScanner.getBoundCodeBinder(nextField);
}
def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et, binder);
def = new RuntimeChildDeclaredExtensionDefinition(nextField, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et, binder);
if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
((RuntimeChildDeclaredExtensionDefinition)def).setEnumerationType(ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next));
((RuntimeChildDeclaredExtensionDefinition)def).setEnumerationType(ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(nextField));
}
} else if (BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) || IBaseReference.class.isAssignableFrom(nextElementType)) {
/*
@ -379,11 +400,11 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
refTypesList.add(myContext.getVersion().getVersion().isRi() ? IAnyResource.class : IResource.class);
continue;
} else if (IBaseResource.class.isAssignableFrom(nextType) == false) {
throw new ConfigurationException("Field '" + next.getName() + "' in class '" + next.getDeclaringClass().getCanonicalName() + "' is of type " + BaseResourceReferenceDt.class + " but contains a non-resource type: " + nextType.getCanonicalName());
throw new ConfigurationException("Field '" + nextField.getName() + "' in class '" + nextField.getDeclaringClass().getCanonicalName() + "' is of type " + BaseResourceReferenceDt.class + " but contains a non-resource type: " + nextType.getCanonicalName());
}
refTypesList.add((Class<? extends IBaseResource>) nextType);
}
def = new RuntimeChildResourceDefinition(next, elementName, childAnnotation, descriptionAnnotation, refTypesList);
def = new RuntimeChildResourceDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, refTypesList);
} else if (IResourceBlock.class.isAssignableFrom(nextElementType) || IBaseBackboneElement.class.isAssignableFrom(nextElementType)
|| IBaseDatatypeElement.class.isAssignableFrom(nextElementType)) {
@ -392,43 +413,43 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
*/
Class<? extends IBase> blockDef = (Class<? extends IBase>) nextElementType;
def = new RuntimeChildResourceBlockDefinition(myContext, next, childAnnotation, descriptionAnnotation, elementName, blockDef);
def = new RuntimeChildResourceBlockDefinition(myContext, nextField, childAnnotation, descriptionAnnotation, elementName, blockDef);
} else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType) || "Type".equals(nextElementType.getSimpleName())
|| IBaseDatatype.class.equals(nextElementType)) {
def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
def = new RuntimeChildAny(nextField, elementName, childAnnotation, descriptionAnnotation);
} else if (IDatatype.class.isAssignableFrom(nextElementType) || IPrimitiveType.class.isAssignableFrom(nextElementType) || ICompositeType.class.isAssignableFrom(nextElementType)
|| IBaseDatatype.class.isAssignableFrom(nextElementType) || IBaseExtension.class.isAssignableFrom(nextElementType)) {
Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
if (IPrimitiveType.class.isAssignableFrom(nextElementType)) {
if (nextElementType.equals(BoundCodeDt.class)) {
IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(next);
Class<? extends Enum<?>> enumType = ModelScanner.determineEnumTypeForBoundField(next);
def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(nextField);
Class<? extends Enum<?>> enumType = ModelScanner.determineEnumTypeForBoundField(nextField);
def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
} else if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
Class<? extends Enum<?>> binderType = ModelScanner.determineEnumTypeForBoundField(next);
def = new RuntimeChildPrimitiveEnumerationDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binderType);
Class<? extends Enum<?>> binderType = ModelScanner.determineEnumTypeForBoundField(nextField);
def = new RuntimeChildPrimitiveEnumerationDatatypeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binderType);
} else {
def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
def = new RuntimeChildPrimitiveDatatypeDefinition(nextField, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
}
} else {
if (IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(next);
Class<? extends Enum<?>> enumType = ModelScanner.determineEnumTypeForBoundField(next);
def = new RuntimeChildCompositeBoundDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(nextField);
Class<? extends Enum<?>> enumType = ModelScanner.determineEnumTypeForBoundField(nextField);
def = new RuntimeChildCompositeBoundDatatypeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
} else if (BaseNarrativeDt.class.isAssignableFrom(nextElementType) || INarrative.class.isAssignableFrom(nextElementType)) {
def = new RuntimeChildNarrativeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
def = new RuntimeChildNarrativeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
} else {
def = new RuntimeChildCompositeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
def = new RuntimeChildCompositeDatatypeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
}
}
} else {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a valid child type: " + nextElementType);
throw new ConfigurationException("Field '" + elementName + "' in type '" + declaringClass.getCanonicalName() + "' is not a valid child type: " + nextElementType);
}
Binding bindingAnnotation = ModelScanner.pullAnnotation(next, Binding.class);
Binding bindingAnnotation = ModelScanner.pullAnnotation(nextField, Binding.class);
if (bindingAnnotation != null) {
if (isNotBlank(bindingAnnotation.valueSet())) {
def.setBindingValueSet(bindingAnnotation.valueSet());
@ -448,7 +469,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
}
mySealed = true;
scanCompositeElementForChildren(getImplementingClass(), this);
scanCompositeElementForChildren();
super.sealAndInitialize(theContext, theClassToElementDefinitions);
@ -537,5 +558,54 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
return index;
}
private static class ScannedField {
private Child myChildAnnotation;
private List<Class<? extends IBase>> myChoiceTypes = new ArrayList<Class<? extends IBase>>();
private Class<?> myElementType;
private Field myField;
private boolean myFirstFieldInNewClass;
public ScannedField(Field theField, Class<?> theClass, boolean theFirstFieldInNewClass) {
myField = theField;
myFirstFieldInNewClass = theFirstFieldInNewClass;
Child childAnnotation = ModelScanner.pullAnnotation(theField, Child.class);
if (childAnnotation == null) {
ourLog.trace("Ignoring non @Child field {} on target type {}", theField.getName(), theClass);
return;
}
if (Modifier.isFinal(theField.getModifiers())) {
ourLog.trace("Ignoring constant {} on target type {}", theField.getName(), theClass);
return;
}
myChildAnnotation = childAnnotation;
myElementType = ModelScanner.determineElementType(theField);
for (Class<? extends IBase> nextChoiceType : childAnnotation.type()) {
myChoiceTypes.add(nextChoiceType);
}
}
public Child getChildAnnotation() {
return myChildAnnotation;
}
public List<Class<? extends IBase>> getChoiceTypes() {
return myChoiceTypes;
}
public Class<?> getElementType() {
return myElementType;
}
public Field getField() {
return myField;
}
public boolean isFirstFieldInNewClass() {
return myFirstFieldInNewClass;
}
}
}

View File

@ -29,6 +29,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.lang3.Validate;
@ -313,7 +314,7 @@ public class FhirContext {
RuntimeResourceDefinition retVal = (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType);
if (retVal == null) {
retVal = scanResourceType((Class<? extends IResource>) theResourceType);
retVal = scanResourceType(theResourceType);
}
return retVal;
}
@ -329,7 +330,8 @@ public class FhirContext {
Map<String, Class<? extends IBaseResource>> nameToType = myVersionToNameToResourceType.get(theVersion);
if (nameToType == null) {
nameToType = new HashMap<String, Class<? extends IBaseResource>>();
ModelScanner.scanVersionPropertyFile(null, nameToType, theVersion);
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> existing = Collections.emptyMap();
ModelScanner.scanVersionPropertyFile(null, nameToType, theVersion, existing);
Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> newVersionToNameToResourceType = new HashMap<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>>();
newVersionToNameToResourceType.putAll(myVersionToNameToResourceType);
@ -585,7 +587,7 @@ public class FhirContext {
return defs.get(theResourceType);
}
private RuntimeResourceDefinition scanResourceType(Class<? extends IResource> theResourceType) {
private RuntimeResourceDefinition scanResourceType(Class<? extends IBaseResource> theResourceType) {
ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<Class<? extends IElement>>();
resourceTypes.add(theResourceType);
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes);
@ -611,11 +613,19 @@ public class FhirContext {
Map<String, BaseRuntimeElementDefinition<?>> nameToElementDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>();
nameToElementDefinition.putAll(myNameToElementDefinition);
nameToElementDefinition.putAll(scanner.getNameToElementDefinitions());
for (Entry<String, BaseRuntimeElementDefinition<?>> next : scanner.getNameToElementDefinitions().entrySet()) {
if (!nameToElementDefinition.containsKey(next.getKey())) {
nameToElementDefinition.put(next.getKey(), next.getValue());
}
}
Map<String, RuntimeResourceDefinition> nameToResourceDefinition = new HashMap<String, RuntimeResourceDefinition>();
nameToResourceDefinition.putAll(myNameToResourceDefinition);
nameToResourceDefinition.putAll(scanner.getNameToResourceDefinition());
for (Entry<String, RuntimeResourceDefinition> next : scanner.getNameToResourceDefinition().entrySet()) {
if (!nameToResourceDefinition.containsKey(next.getKey())) {
nameToResourceDefinition.put(next.getKey(), next.getValue());
}
}
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> classToElementDefinition = new HashMap<Class<? extends IBase>, BaseRuntimeElementDefinition<?>>();
classToElementDefinition.putAll(myClassToElementDefinition);

View File

@ -150,7 +150,7 @@ class ModelScanner {
return myRuntimeChildUndeclaredExtensionDefinition;
}
private void init(Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Set<Class<? extends IBase>> theDatatypes) {
private void init(Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Set<Class<? extends IBase>> theTypesToScan) {
if (theExistingDefinitions != null) {
myClassToElementDefinitions.putAll(theExistingDefinitions);
}
@ -159,17 +159,11 @@ class ModelScanner {
long start = System.currentTimeMillis();
Map<String, Class<? extends IBaseResource>> resourceTypes = myNameToResourceType;
myVersionTypes = scanVersionPropertyFile(theDatatypes, resourceTypes, myVersion);
// toScan.add(DateDt.class);
// toScan.add(CodeDt.class);
// toScan.add(DecimalDt.class);
// toScan.add(AttachmentDt.class);
// toScan.add(ResourceReferenceDt.class);
// toScan.add(QuantityDt.class);
Set<Class<? extends IBase>> typesToScan = theTypesToScan;
myVersionTypes = scanVersionPropertyFile(typesToScan, resourceTypes, myVersion, myClassToElementDefinitions);
do {
for (Class<? extends IBase> nextClass : theDatatypes) {
for (Class<? extends IBase> nextClass : typesToScan) {
scan(nextClass);
}
for (Iterator<Class<? extends IBase>> iter = myScanAlso.iterator(); iter.hasNext();) {
@ -177,10 +171,10 @@ class ModelScanner {
iter.remove();
}
}
theDatatypes.clear();
theDatatypes.addAll(myScanAlso);
typesToScan.clear();
typesToScan.addAll(myScanAlso);
myScanAlso.clear();
} while (!theDatatypes.isEmpty());
} while (!typesToScan.isEmpty());
for (Entry<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> nextEntry : myClassToElementDefinitions.entrySet()) {
if (theExistingDefinitions != null && theExistingDefinitions.containsKey(nextEntry.getKey())) {
@ -237,6 +231,7 @@ class ModelScanner {
@SuppressWarnings("unchecked")
Class<? extends IBaseResource> resClass = (Class<? extends IBaseResource>) theClass;
scanResource(resClass, resourceDefinition);
return;
}
DatatypeDef datatypeDefinition = pullAnnotation(theClass, DatatypeDef.class);
@ -249,11 +244,9 @@ class ModelScanner {
@SuppressWarnings({ "unchecked" })
Class<? extends IPrimitiveType<?>> resClass = (Class<? extends IPrimitiveType<?>>) theClass;
scanPrimitiveDatatype(resClass, datatypeDefinition);
} else {
return;
// throw new ConfigurationException("Resource type contains a @" + DatatypeDef.class.getSimpleName() + " annotation but does not implement " + IDatatype.class.getCanonicalName() + ": " +
// theClass.getCanonicalName());
}
}
return;
}
Block blockDefinition = pullAnnotation(theClass, Block.class);
@ -297,6 +290,13 @@ class ModelScanner {
}
myClassToElementDefinitions.put(theClass, elementDef);
myNameToElementDefinitions.put(elementDef.getName().toLowerCase(), elementDef);
/*
* See #423:
* If the type contains a field that has a custom type, we want to make
* sure that this type gets scanned as well
*/
elementDef.populateScanAlso(myScanAlso);
}
@ -364,7 +364,8 @@ class ModelScanner {
}
}
Class<? extends IBaseResource> builtInType = myNameToResourceType.get(resourceName.toLowerCase());
String resourceNameLowerCase = resourceName.toLowerCase();
Class<? extends IBaseResource> builtInType = myNameToResourceType.get(resourceNameLowerCase);
boolean standardType = builtInType != null && builtInType.equals(theClass) == true;
if (primaryNameProvider) {
if (builtInType != null && builtInType.equals(theClass) == false) {
@ -384,7 +385,7 @@ class ModelScanner {
myClassToElementDefinitions.put(theClass, resourceDef);
if (primaryNameProvider) {
if (resourceDef.getStructureVersion() == myVersion) {
myNameToResourceDefinitions.put(resourceName.toLowerCase(), resourceDef);
myNameToResourceDefinitions.put(resourceNameLowerCase, resourceDef);
}
}
@ -392,6 +393,13 @@ class ModelScanner {
scanResourceForSearchParams(theClass, resourceDef);
/*
* See #423:
* If the type contains a field that has a custom type, we want to make
* sure that this type gets scanned as well
*/
resourceDef.populateScanAlso(myScanAlso);
return resourceName;
}
@ -482,10 +490,10 @@ class ModelScanner {
return type;
}
static Set<Class<? extends IBase>> scanVersionPropertyFile(Set<Class<? extends IBase>> theDatatypes, Map<String, Class<? extends IBaseResource>> theResourceTypes, FhirVersionEnum version) {
static Set<Class<? extends IBase>> scanVersionPropertyFile(Set<Class<? extends IBase>> theDatatypes, Map<String, Class<? extends IBaseResource>> theResourceTypes, FhirVersionEnum theVersion, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingElementDefinitions) {
Set<Class<? extends IBase>> retVal = new HashSet<Class<? extends IBase>>();
InputStream str = version.getVersionImplementation().getFhirVersionPropertiesFile();
InputStream str = theVersion.getVersionImplementation().getFhirVersionPropertiesFile();
Properties prop = new Properties();
try {
prop.load(str);
@ -500,6 +508,9 @@ class ModelScanner {
@SuppressWarnings("unchecked")
Class<? extends IBase> dtType = (Class<? extends IBase>) Class.forName(nextValue);
if (theExistingElementDefinitions.containsKey(dtType)) {
continue;
}
retVal.add(dtType);
if (IElement.class.isAssignableFrom(dtType)) {
@ -525,6 +536,9 @@ class ModelScanner {
try {
@SuppressWarnings("unchecked")
Class<? extends IBaseResource> nextClass = (Class<? extends IBaseResource>) Class.forName(nextValue);
if (theExistingElementDefinitions.containsKey(nextClass)) {
continue;
}
if (!IBaseResource.class.isAssignableFrom(nextClass)) {
throw new ConfigurationException("Class is not assignable from " + IBaseResource.class.getSimpleName() + ": " + nextValue);
}

View File

@ -53,13 +53,16 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
myResourceProfile = theResourceAnnotation.profile();
myId = theResourceAnnotation.id();
IBaseResource instance;
try {
IBaseResource instance = theClass.newInstance();
myStructureVersion = instance.getStructureFhirVersionEnum();
assert myStructureVersion != null;
instance = theClass.newInstance();
} catch (Exception e) {
throw new ConfigurationException(myContext.getLocalizer().getMessage(getClass(), "nonInstantiableType", theClass.getName(), e.toString()), e);
}
myStructureVersion = instance.getStructureFhirVersionEnum();
if (myStructureVersion != theContext.getVersion().getVersion()) {
throw new ConfigurationException(myContext.getLocalizer().getMessage(getClass(), "typeWrongVersion", theContext.getVersion().getVersion(), theClass.getName(), myStructureVersion));
}
}

View File

@ -623,13 +623,15 @@ public abstract class BaseParser implements IParser {
@Override
public <T extends IBaseResource> T parseResource(Class<T> theResourceType, Reader theReader) throws DataFormatException {
/*
* We do this so that the context can verify that the structure is for
* the correct FHIR version
*/
if (theResourceType != null) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResourceType);
if (def.getStructureVersion() != myContext.getVersion().getVersion()) {
throw new IllegalArgumentException("This parser is for FHIR version " + myContext.getVersion().getVersion() + " - Can not parse a structure for version " + def.getStructureVersion());
}
myContext.getResourceDefinition(theResourceType);
}
// Actually do the parse
T retVal = doParseResource(theResourceType, theReader);
RuntimeResourceDefinition def = myContext.getResourceDefinition(retVal);

View File

@ -6,6 +6,7 @@ ca.uhn.fhir.context.FhirContext.noStructures=Could not find any HAPI-FHIR struct
ca.uhn.fhir.context.FhirContext.noStructuresForSpecifiedVersion=Could not find the HAPI-FHIR structure JAR on the classpath for version {0}. Note that as of HAPI-FHIR v0.8, a separate FHIR strcture JAR must be added to your classpath (or project pom.xml if you are using Maven)
ca.uhn.fhir.context.RuntimeResourceDefinition.nonInstantiableType=Resource type "{0}" can not be instantiated. Check that this class has a no-argument constructor, and that it is static if it is a nested type. Error is: {1}
ca.uhn.fhir.context.RuntimeResourceDefinition.typeWrongVersion=This context is for FHIR version "{0}" but the class "{1}" is for version "{2}"
ca.uhn.fhir.rest.client.BaseClient.ioExceptionDuringOperation=Encountered IOException when performing {0} to URL {1} - {2}
ca.uhn.fhir.rest.client.BaseClient.failedToParseResponse=Failed to parse response from server when performing {0} to URL {1} - {2}

View File

@ -760,7 +760,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
@Test
public void testDocumentManifestResources() throws Exception {
myFhirCtx.getResourceDefinition(Practitioner.class);
myFhirCtx.getResourceDefinition(ca.uhn.fhir.model.dstu.resource.DocumentManifest.class);
myFhirCtx.getResourceDefinition(DocumentManifest.class);
IGenericClient client = ourClient;
@ -2254,7 +2254,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
input.addParameter().setName("resource").setResource(patient);
String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(input);
ourLog.info(inputStr);
// ourLog.info(inputStr);
HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate");
post.setEntity(new StringEntity(inputStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));

View File

@ -1,11 +1,12 @@
package ca.uhn.fhir.validator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse;
import ca.uhn.fhir.model.primitive.DateTimeDt;
@ -24,6 +25,18 @@ public class ValidatorAcrossVersionsTest {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testWrongContextVersion() {
FhirContext ctxDstu2 = FhirContext.forDstu2();
try {
ctxDstu2.getResourceDefinition(org.hl7.fhir.dstu3.model.Patient.class);
fail();
} catch (ConfigurationException e) {
assertEquals("This context is for FHIR version \"DSTU2\" but the class \"org.hl7.fhir.dstu3.model.Patient\" is for version \"DSTU3\"", e.getMessage());
}
}
@Test
public void testValidateProfileOnDstu2Resource() {

View File

@ -38,7 +38,7 @@
<appender-ref ref="STDOUT" />
</logger>
<!-- Set to 'trace' to enable SQL Value logging -->
<logger name="org.hibernate.type" additivity="false" level="trace">
<logger name="org.hibernate.type" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>

View File

@ -956,7 +956,7 @@ public class RestfulServerMethodTest {
IdDt id = new IdDt("Profile", "observation");
Profile profile = profileProvider.getProfileById(createHttpServletRequest(), id);
assertNull(profile);
assertNotNull(profile);
id = new IdDt("Profile", "patient");
profile = profileProvider.getProfileById(createHttpServletRequest(), id);

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.parser.i423;
import java.util.List;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu2.resource.ProcedureRequest;
import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name = "ProcedureRequest", id = "custom-procedure-request", profile = "http://test/")
public class CustomProcedureRequest extends ProcedureRequest {
private static final long serialVersionUID = 1L;
/**
* scheduled
*/
@Child(name = FIELD_SCHEDULED, min = 0, max = 1, order = Child.REPLACE_PARENT, type = { CustomTimingDt.class })
@Description(shortDefinition = "When procedure should occur", formalDefinition = "The timing schedule for the proposed or ordered procedure. The Schedule data type allows many different expressions. E.g. \"Every 8 hours\"; \"Three times a day\"; \"1/2 an hour before breakfast for 10 days from 23-Dec 2011:\"; \"15 Oct 2013, 17 Oct 2013 and 1 Nov 2013\".")
protected CustomTimingDt ourScheduled;
public static final String FIELD_SCHEDULED = "scheduled";
public CustomTimingDt _getScheduled() {
if (ourScheduled == null)
ourScheduled = new CustomTimingDt();
return ourScheduled;
}
public CustomProcedureRequest _setScheduled(CustomTimingDt theValue) {
ourScheduled = theValue;
return this;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(ourScheduled);
}
@Override
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
return ElementUtil.allPopulatedChildElements(theType, ourScheduled);
}
}

View File

@ -0,0 +1,43 @@
package ca.uhn.fhir.parser.i423;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IntegerDt;
public class CustomProcedureRequestTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomProcedureRequestTest.class);
@Test
public void testCreate() {
FhirContext ctx = FhirContext.forDstu2();
CustomProcedureRequest procedureRequest = new CustomProcedureRequest();
CustomTimingDt timingDt = new CustomTimingDt();
CustomTimingDt._Repeat repeat = new CustomTimingDt._Repeat();
repeat._setFrequency(new IntegerDt(2));
timingDt._setRepeat(repeat);
procedureRequest._setScheduled(timingDt);
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(procedureRequest);
ourLog.info(encoded);
//@formatter:off
assertThat(encoded, stringContainsInOrder(
"<ProcedureRequest xmlns=\"http://hl7.org/fhir\">",
"<meta>",
"<profile value=\"http://test/\"/>",
"</meta>",
"<scheduledTiming>",
"<repeat>",
"<frequency value=\"2\"/>",
"</repeat>",
"</scheduledTiming>",
"</ProcedureRequest>"));
//@formatter:on
}
}

View File

@ -0,0 +1,565 @@
package ca.uhn.fhir.parser.i423;
import java.math.BigDecimal;
import java.util.List;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
import ca.uhn.fhir.model.dstu2.composite.RangeDt;
import ca.uhn.fhir.model.dstu2.composite.TimingDt;
import ca.uhn.fhir.model.dstu2.valueset.EventTimingEnum;
import ca.uhn.fhir.model.dstu2.valueset.UnitsOfTimeEnum;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.util.ElementUtil;
@DatatypeDef(name = "Timing")
public class CustomTimingDt extends TimingDt {
/**
* repeat
*/
@Child(name = FIELD_REPEAT, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {_Repeat.class})
@Description(shortDefinition = "When the event is to occur", formalDefinition = "A set of rules that describe when the event should occur.")
protected _Repeat ourRepeat;
public static final String FIELD_REPEAT = "repeat";
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(ourRepeat);
}
@Override
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
return ElementUtil.allPopulatedChildElements(theType, ourRepeat);
}
public _Repeat _getRepeat() {
if (ourRepeat == null)
ourRepeat = new _Repeat();
return ourRepeat;
}
public CustomTimingDt _setRepeat(_Repeat theValue) {
ourRepeat = theValue;
return this;
}
@Block
public static class _Repeat extends Repeat {
/**
* bounds
*/
@Child(name = FIELD_BOUNDS, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {RangeDt.class, PeriodDt.class})
@Description(shortDefinition = "Length/Range of lengths, or (Start and/or end) limits", formalDefinition = "Either a duration for the length of the timing schedule, a range of possible length, or outer bounds for start and/or end limits of the timing schedule.")
protected IDatatype ourBounds;
public static final String FIELD_BOUNDS = "bounds";
/**
* count
*/
@Child(name = FIELD_COUNT, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {IntegerDt.class})
@Description(shortDefinition = "Number of times to repeat", formalDefinition = "A total count of the desired number of repetitions.")
protected IntegerDt ourCount;
public static final String FIELD_COUNT = "count";
/**
* duration
*/
@Child(name = FIELD_DURATION, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {DecimalDt.class})
@Description(shortDefinition = "How long when it happens", formalDefinition = "How long this thing happens for when it happens.")
protected DecimalDt ourDuration;
public static final String FIELD_DURATION = "duration";
/**
* durationMax
*/
@Child(name = FIELD_DURATIONMAX, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {DecimalDt.class})
@Description(shortDefinition = "How long when it happens (Max)", formalDefinition = "The upper limit of how long this thing happens for when it happens.")
protected DecimalDt ourDurationMax;
public static final String FIELD_DURATIONMAX = "durationMax";
/**
* durationUnits
*/
@Child(name = FIELD_DURATIONUNITS, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {CodeDt.class})
@Description(shortDefinition = "s | min | h | d | wk | mo | a - unit of time (UCUM)", formalDefinition = "The units of time for the duration, in UCUM units.")
protected BoundCodeDt<UnitsOfTimeEnum> ourDurationUnits;
public static final String FIELD_DURATIONUNITS = "durationUnits";
/**
* frequency
*/
@Child(name = FIELD_FREQUENCY, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {IntegerDt.class})
@Description(shortDefinition = "Event occurs frequency times per period", formalDefinition = "The number of times to repeat the action within the specified period / period range (i.e. both period and periodMax provided).")
protected IntegerDt ourFrequency;
public static final String FIELD_FREQUENCY = "frequency";
/**
* frequencyMax
*/
@Child(name = FIELD_FREQUENCYMAX, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {IntegerDt.class})
@Description(shortDefinition = "Event occurs up to frequencyMax times per period", formalDefinition = "If present, indicates that the frequency is a range - so repeat between [frequency] and [frequencyMax] times within the period or period range.")
protected IntegerDt ourFrequencyMax;
public static final String FIELD_FREQUENCYMAX = "frequencyMax";
/**
* period
*/
@Child(name = FIELD_PERIOD, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {DecimalDt.class})
@Description(shortDefinition = "Event occurs frequency times per period", formalDefinition = "Indicates the duration of time over which repetitions are to occur; e.g. to express \"3 times per day\", 3 would be the frequency and \"1 day\" would be the period.")
protected DecimalDt ourPeriod;
public static final String FIELD_PERIOD = "period";
/**
* periodMax
*/
@Child(name = FIELD_PERIODMAX, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {DecimalDt.class})
@Description(shortDefinition = "Upper limit of period (3-4 hours)", formalDefinition = "If present, indicates that the period is a range from [period] to [periodMax], allowing expressing concepts such as \"do this once every 3-5 days.")
protected DecimalDt ourPeriodMax;
public static final String FIELD_PERIODMAX = "periodMax";
/**
* periodUnits
*/
@Child(name = FIELD_PERIODUNITS, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {CodeDt.class})
@Description(shortDefinition = "s | min | h | d | wk | mo | a - unit of time (UCUM)", formalDefinition = "The units of time for the period in UCUM units.")
protected BoundCodeDt<UnitsOfTimeEnum> ourPeriodUnits;
public static final String FIELD_PERIODUNITS = "periodUnits";
/**
* when
*/
@Child(name = FIELD_WHEN, min = 0, max = 1, order = Child.REPLACE_PARENT, summary = true, type = {CodeDt.class})
@Description(shortDefinition = "Regular life events the event is tied to", formalDefinition = "A real world event that the occurrence of the event should be tied to.")
protected BoundCodeDt<EventTimingEnum> ourWhen;
public static final String FIELD_WHEN = "when";
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(ourBounds, ourCount, ourDuration, ourDurationMax, ourDurationUnits, ourFrequency, ourFrequencyMax, ourPeriod, ourPeriodMax, ourPeriodUnits, ourWhen);
}
@Override
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
return ElementUtil.allPopulatedChildElements(theType, ourBounds, ourCount, ourDuration, ourDurationMax, ourDurationUnits, ourFrequency, ourFrequencyMax, ourPeriod, ourPeriodMax, ourPeriodUnits, ourWhen);
}
public IDatatype _getBounds() {
return ourBounds;
}
public _Repeat _setBounds(IDatatype theValue) {
ourBounds = theValue;
return this;
}
public IntegerDt _getCount() {
if (ourCount == null)
ourCount = new IntegerDt();
return ourCount;
}
public _Repeat _setCount(IntegerDt theValue) {
ourCount = theValue;
return this;
}
public DecimalDt _getDuration() {
if (ourDuration == null)
ourDuration = new DecimalDt();
return ourDuration;
}
public _Repeat _setDuration(DecimalDt theValue) {
ourDuration = theValue;
return this;
}
public DecimalDt _getDurationMax() {
if (ourDurationMax == null)
ourDurationMax = new DecimalDt();
return ourDurationMax;
}
public _Repeat _setDurationMax(DecimalDt theValue) {
ourDurationMax = theValue;
return this;
}
public BoundCodeDt<UnitsOfTimeEnum> _getDurationUnits() {
if (ourDurationUnits == null)
ourDurationUnits = new BoundCodeDt<UnitsOfTimeEnum>(UnitsOfTimeEnum.VALUESET_BINDER);
return ourDurationUnits;
}
public _Repeat _setDurationUnits(BoundCodeDt<UnitsOfTimeEnum> theValue) {
ourDurationUnits = theValue;
return this;
}
public IntegerDt _getFrequency() {
if (ourFrequency == null)
ourFrequency = new IntegerDt();
return ourFrequency;
}
public _Repeat _setFrequency(IntegerDt theValue) {
ourFrequency = theValue;
return this;
}
public IntegerDt _getFrequencyMax() {
if (ourFrequencyMax == null)
ourFrequencyMax = new IntegerDt();
return ourFrequencyMax;
}
public _Repeat _setFrequencyMax(IntegerDt theValue) {
ourFrequencyMax = theValue;
return this;
}
public DecimalDt _getPeriod() {
if (ourPeriod == null)
ourPeriod = new DecimalDt();
return ourPeriod;
}
public _Repeat _setPeriod(DecimalDt theValue) {
ourPeriod = theValue;
return this;
}
public DecimalDt _getPeriodMax() {
if (ourPeriodMax == null)
ourPeriodMax = new DecimalDt();
return ourPeriodMax;
}
public _Repeat _setPeriodMax(DecimalDt theValue) {
ourPeriodMax = theValue;
return this;
}
public BoundCodeDt<UnitsOfTimeEnum> _getPeriodUnits() {
if (ourPeriodUnits == null)
ourPeriodUnits = new BoundCodeDt<UnitsOfTimeEnum>(UnitsOfTimeEnum.VALUESET_BINDER);
return ourPeriodUnits;
}
public _Repeat _setPeriodUnits(BoundCodeDt<UnitsOfTimeEnum> theValue) {
ourPeriodUnits = theValue;
return this;
}
public BoundCodeDt<EventTimingEnum> _getWhen() {
if (ourWhen == null)
ourWhen = new BoundCodeDt<EventTimingEnum>(EventTimingEnum.VALUESET_BINDER);
return ourWhen;
}
public _Repeat _setWhen(BoundCodeDt<EventTimingEnum> theValue) {
ourWhen = theValue;
return this;
}
@Override
@Deprecated
public IDatatype getBounds() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setBounds(IDatatype p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setCount(IntegerDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setCount(int p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDuration(DecimalDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDuration(double p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDuration(BigDecimal p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDuration(long p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDurationMax(DecimalDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDurationMax(double p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDurationMax(BigDecimal p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDurationMax(long p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDurationUnits(UnitsOfTimeEnum p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setDurationUnits(BoundCodeDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setFrequency(IntegerDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setFrequency(int p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setFrequencyMax(IntegerDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setFrequencyMax(int p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriod(DecimalDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriod(double p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriod(BigDecimal p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriod(long p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriodMax(DecimalDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriodMax(double p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriodMax(BigDecimal p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriodMax(long p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriodUnits(UnitsOfTimeEnum p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setPeriodUnits(BoundCodeDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setWhen(EventTimingEnum p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Repeat setWhen(BoundCodeDt p0) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public BoundCodeDt getDurationUnitsElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public BoundCodeDt getPeriodUnitsElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public BoundCodeDt getWhenElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public DecimalDt getDurationElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public DecimalDt getDurationMaxElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public DecimalDt getPeriodElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public DecimalDt getPeriodMaxElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public IntegerDt getCountElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public IntegerDt getFrequencyElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public IntegerDt getFrequencyMaxElement() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Integer getCount() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Integer getFrequency() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public Integer getFrequencyMax() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public String getDurationUnits() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public String getPeriodUnits() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public String getWhen() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public BigDecimal getDuration() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public BigDecimal getDurationMax() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public BigDecimal getPeriod() {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
@Deprecated
public BigDecimal getPeriodMax() {
throw new UnsupportedOperationException("Deprecated method");
}
}
}

View File

@ -9,9 +9,6 @@ import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.junit.AfterClass;
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.rest.client.MyPatientWithExtensions;
import ca.uhn.fhir.util.TestUtil;
@ -24,7 +21,7 @@ public class FhirContextDstu3Test {
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
/**
* See #344
*/

View File

@ -31,6 +31,7 @@ public class ResourceWithExtensionsDstu3A extends BaseResource {
* so check the unit tests immediately after any changes
*/
private static final long serialVersionUID = 1L;
@Child(name = "foo1", type = StringType.class, order = 0, min = 0, max = Child.MAX_UNLIMITED)
@Extension(url = "http://foo/#f1", definedLocally=true, isModifier=false)
@ -197,11 +198,6 @@ public class ResourceWithExtensionsDstu3A extends BaseResource {
@Override
public FhirVersionEnum getStructureFhirVersionEnum() {
return FhirVersionEnum.DSTU1;
}
@Override
public String getId() {
return null;

View File

@ -2,12 +2,7 @@ package ca.uhn.fhir.rest.client;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -144,6 +139,16 @@ public class GenericClientDstu3Test {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
MyPatientWithExtensions read = client.read().resource(MyPatientWithExtensions.class).withId(new IdType("1")).execute();
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div>", read.getText().getDivAsString());
// Ensure that we haven't overridden the default type for the name
assertFalse(MyPatientWithExtensions.class.isAssignableFrom(Patient.class));
assertFalse(Patient.class.isAssignableFrom(MyPatientWithExtensions.class));
Patient pt = new Patient();
pt.getText().setDivAsString("A PATIENT");
IParser parser = ourCtx.newXmlParser();
String encoded = parser.encodeResourceToString(pt);
pt = (Patient) parser.parseResource(encoded);
}
private byte[] extractBodyAsByteArray(ArgumentCaptor<HttpUriRequest> capt) throws IOException {

View File

@ -15,26 +15,6 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder (5).launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder (6).launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>

View File

@ -156,6 +156,11 @@
"-b FOO" that lets you add an authorization header in the form
<![CDATA[<code>Authorization: Bearer FOO</code>]]>
</action>
<action type="fix" issue="423">
Parser failed to successfully encode a custom resource
if it contained custom fields that also used custom
types. Thanks to GitHub user @sjanic for reporting!
</action>
</release>
<release version="1.6" date="2016-07-07">
<action type="fix">