Improve parallel model scanning (#3046)
* Improve parallel model scanning * Add changelog * Resolve fixme * Test fix
This commit is contained in:
parent
53a2d3c1ac
commit
95e853bdf7
|
@ -1,57 +1,5 @@
|
||||||
package ca.uhn.fhir.context;
|
package ca.uhn.fhir.context;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ListIterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.ICompositeType;
|
|
||||||
import org.hl7.fhir.instance.model.api.INarrative;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.IBoundCodeableConcept;
|
import ca.uhn.fhir.model.api.IBoundCodeableConcept;
|
||||||
import ca.uhn.fhir.model.api.IDatatype;
|
import ca.uhn.fhir.model.api.IDatatype;
|
||||||
import ca.uhn.fhir.model.api.IElement;
|
import ca.uhn.fhir.model.api.IElement;
|
||||||
|
@ -69,26 +17,55 @@ import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||||
|
import org.hl7.fhir.instance.model.api.INarrative;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> extends BaseRuntimeElementDefinition<T> {
|
public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> extends BaseRuntimeElementDefinition<T> {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseRuntimeElementCompositeDefinition.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseRuntimeElementCompositeDefinition.class);
|
||||||
|
private final FhirContext myContext;
|
||||||
private Map<String, Integer> forcedOrder = null;
|
private Map<String, Integer> forcedOrder = null;
|
||||||
private List<BaseRuntimeChildDefinition> myChildren = new ArrayList<>();
|
private List<BaseRuntimeChildDefinition> myChildren = new ArrayList<>();
|
||||||
private List<BaseRuntimeChildDefinition> myChildrenAndExtensions;
|
private List<BaseRuntimeChildDefinition> myChildrenAndExtensions;
|
||||||
private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions;
|
private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions;
|
||||||
private final FhirContext myContext;
|
|
||||||
private Map<String, BaseRuntimeChildDefinition> myNameToChild = new HashMap<>();
|
private Map<String, BaseRuntimeChildDefinition> myNameToChild = new HashMap<>();
|
||||||
private List<ScannedField> myScannedFields = new ArrayList<>();
|
private List<ScannedField> myScannedFields = new ArrayList<>();
|
||||||
private volatile boolean mySealed;
|
private volatile SealingStateEnum mySealed = SealingStateEnum.NOT_SEALED;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public BaseRuntimeElementCompositeDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType, FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
public BaseRuntimeElementCompositeDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType, FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||||
super(theName, theImplementingClass, theStandardType);
|
super(theName, theImplementingClass, theStandardType);
|
||||||
|
|
||||||
myContext = theContext;
|
myContext = theContext;
|
||||||
myClassToElementDefinitions = theClassToElementDefinitions;
|
myClassToElementDefinitions = theClassToElementDefinitions;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We scan classes for annotated fields in the class but also all of its superclasses
|
* We scan classes for annotated fields in the class but also all of its superclasses
|
||||||
*/
|
*/
|
||||||
|
@ -108,7 +85,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
classes .push(current);
|
classes.push(current);
|
||||||
if (IBase.class.isAssignableFrom(current.getSuperclass())) {
|
if (IBase.class.isAssignableFrom(current.getSuperclass())) {
|
||||||
current = (Class<? extends IBase>) current.getSuperclass();
|
current = (Class<? extends IBase>) current.getSuperclass();
|
||||||
} else {
|
} else {
|
||||||
|
@ -143,7 +120,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseRuntimeChildDefinition getChildByName(String theName){
|
public BaseRuntimeChildDefinition getChildByName(String theName) {
|
||||||
validateSealed();
|
validateSealed();
|
||||||
return myNameToChild.get(theName);
|
return myNameToChild.get(theName);
|
||||||
}
|
}
|
||||||
|
@ -162,8 +139,8 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
validateSealed();
|
validateSealed();
|
||||||
return myChildren;
|
return myChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<BaseRuntimeChildDefinition> getChildrenAndExtension() {
|
public List<BaseRuntimeChildDefinition> getChildrenAndExtension() {
|
||||||
validateSealed();
|
validateSealed();
|
||||||
return myChildrenAndExtensions;
|
return myChildrenAndExtensions;
|
||||||
|
@ -174,7 +151,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
* Has this class been sealed
|
* Has this class been sealed
|
||||||
*/
|
*/
|
||||||
public boolean isSealed() {
|
public boolean isSealed() {
|
||||||
return mySealed;
|
return mySealed == SealingStateEnum.SEALED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -192,7 +169,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scanCompositeElementForChildren() {
|
private void scanCompositeElementForChildren() {
|
||||||
Set<String> elementNames = new HashSet<>();
|
Set<String> elementNames = new HashSet<>();
|
||||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToElementDef = new TreeMap<>();
|
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToElementDef = new TreeMap<>();
|
||||||
|
@ -201,7 +178,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
scanCompositeElementForChildren(elementNames, orderToElementDef, orderToExtensionDef);
|
scanCompositeElementForChildren(elementNames, orderToElementDef, orderToExtensionDef);
|
||||||
|
|
||||||
if (forcedOrder != null) {
|
if (forcedOrder != null) {
|
||||||
/*
|
/*
|
||||||
* Find out how many elements don't match any entry in the list
|
* Find out how many elements don't match any entry in the list
|
||||||
* for forced order. Those elements come first.
|
* for forced order. Those elements come first.
|
||||||
*/
|
*/
|
||||||
|
@ -221,7 +198,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
}
|
}
|
||||||
orderToElementDef = newOrderToExtensionDef;
|
orderToElementDef = newOrderToExtensionDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeSet<Integer> orders = new TreeSet<>();
|
TreeSet<Integer> orders = new TreeSet<>();
|
||||||
orders.addAll(orderToElementDef.keySet());
|
orders.addAll(orderToElementDef.keySet());
|
||||||
orders.addAll(orderToExtensionDef.keySet());
|
orders.addAll(orderToExtensionDef.keySet());
|
||||||
|
@ -238,18 +215,18 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void scanCompositeElementForChildren(Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
|
private void scanCompositeElementForChildren(Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
|
||||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
|
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
|
||||||
int baseElementOrder = 0;
|
int baseElementOrder = 0;
|
||||||
|
|
||||||
for (ScannedField next : myScannedFields) {
|
for (ScannedField next : myScannedFields) {
|
||||||
if (next.isFirstFieldInNewClass()) {
|
if (next.isFirstFieldInNewClass()) {
|
||||||
baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
|
baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> declaringClass = next.getField().getDeclaringClass();
|
Class<?> declaringClass = next.getField().getDeclaringClass();
|
||||||
|
|
||||||
Description descriptionAnnotation = ModelScanner.pullAnnotation(next.getField(), Description.class);
|
Description descriptionAnnotation = ModelScanner.pullAnnotation(next.getField(), Description.class);
|
||||||
|
|
||||||
|
@ -266,9 +243,9 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
boolean childIsChoiceType = false;
|
boolean childIsChoiceType = false;
|
||||||
boolean orderIsReplaceParent = false;
|
boolean orderIsReplaceParent = false;
|
||||||
BaseRuntimeChildDefinition replacedParent = null;
|
BaseRuntimeChildDefinition replacedParent = null;
|
||||||
|
|
||||||
if (order == Child.REPLACE_PARENT) {
|
if (order == Child.REPLACE_PARENT) {
|
||||||
|
|
||||||
if (extensionAttr != null) {
|
if (extensionAttr != null) {
|
||||||
|
|
||||||
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
|
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
|
||||||
|
@ -285,7 +262,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
}
|
}
|
||||||
if (order == Child.REPLACE_PARENT) {
|
if (order == Child.REPLACE_PARENT) {
|
||||||
throw new ConfigurationException("Field " + nextField.getName() + "' on target type " + declaringClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
|
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());
|
+ ") but no parent element with extension URL " + extensionAttr.url() + " could be found on type " + nextField.getDeclaringClass().getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -298,7 +275,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
BaseRuntimeDeclaredChildDefinition existing = orderMap.remove(nextEntry.getKey());
|
BaseRuntimeDeclaredChildDefinition existing = orderMap.remove(nextEntry.getKey());
|
||||||
replacedParent = existing;
|
replacedParent = existing;
|
||||||
elementNames.remove(elementName);
|
elementNames.remove(elementName);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See #350 - If the original field (in the superclass) with the given name is a choice, then we need to make sure
|
* See #350 - If the original field (in the superclass) with the given name is a choice, then we need to make sure
|
||||||
* that the field which replaces is a choice even if it's only a choice of one type - this is because the
|
* that the field which replaces is a choice even if it's only a choice of one type - this is because the
|
||||||
|
@ -312,17 +289,17 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
}
|
}
|
||||||
if (order == Child.REPLACE_PARENT) {
|
if (order == Child.REPLACE_PARENT) {
|
||||||
throw new ConfigurationException("Field " + nextField.getName() + "' on target type " + declaringClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
|
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());
|
+ ") but no parent element with name " + elementName + " could be found on type " + nextField.getDeclaringClass().getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order < 0 && order != Child.ORDER_UNKNOWN) {
|
if (order < 0 && order != Child.ORDER_UNKNOWN) {
|
||||||
throw new ConfigurationException("Invalid order '" + order + "' on @Child for field '" + nextField.getName() + "' on target type: " + declaringClass);
|
throw new ConfigurationException("Invalid order '" + order + "' on @Child for field '" + nextField.getName() + "' on target type: " + declaringClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order != Child.ORDER_UNKNOWN && !orderIsReplaceParent) {
|
if (order != Child.ORDER_UNKNOWN && !orderIsReplaceParent) {
|
||||||
order = order + baseElementOrder;
|
order = order + baseElementOrder;
|
||||||
}
|
}
|
||||||
|
@ -340,7 +317,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Class<? extends IBase>> choiceTypes = next.getChoiceTypes();
|
List<Class<? extends IBase>> choiceTypes = next.getChoiceTypes();
|
||||||
|
|
||||||
if (orderMap.containsKey(order)) {
|
if (orderMap.containsKey(order)) {
|
||||||
throw new ConfigurationException("Detected duplicate field order '" + childAnnotation.order() + "' for element named '" + elementName + "' in type '" + declaringClass.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());
|
||||||
}
|
}
|
||||||
|
@ -381,10 +358,10 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
binder = ModelScanner.getBoundCodeBinder(nextField);
|
binder = ModelScanner.getBoundCodeBinder(nextField);
|
||||||
}
|
}
|
||||||
|
|
||||||
def = new RuntimeChildDeclaredExtensionDefinition(nextField, 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)) {
|
if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
|
||||||
((RuntimeChildDeclaredExtensionDefinition)def).setEnumerationType(ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(nextField));
|
((RuntimeChildDeclaredExtensionDefinition) def).setEnumerationType(ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(nextField));
|
||||||
}
|
}
|
||||||
} else if (BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) || IBaseReference.class.isAssignableFrom(nextElementType)) {
|
} else if (BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) || IBaseReference.class.isAssignableFrom(nextElementType)) {
|
||||||
/*
|
/*
|
||||||
|
@ -403,7 +380,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
def = new RuntimeChildResourceDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, refTypesList);
|
def = new RuntimeChildResourceDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, refTypesList);
|
||||||
|
|
||||||
} else if (IResourceBlock.class.isAssignableFrom(nextElementType) || IBaseBackboneElement.class.isAssignableFrom(nextElementType)
|
} else if (IResourceBlock.class.isAssignableFrom(nextElementType) || IBaseBackboneElement.class.isAssignableFrom(nextElementType)
|
||||||
|| IBaseDatatypeElement.class.isAssignableFrom(nextElementType)) {
|
|| IBaseDatatypeElement.class.isAssignableFrom(nextElementType)) {
|
||||||
/*
|
/*
|
||||||
* Child is a resource block (i.e. a sub-tag within a resource) TODO: do these have a better name according to HL7?
|
* Child is a resource block (i.e. a sub-tag within a resource) TODO: do these have a better name according to HL7?
|
||||||
*/
|
*/
|
||||||
|
@ -411,11 +388,11 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
Class<? extends IBase> blockDef = (Class<? extends IBase>) nextElementType;
|
Class<? extends IBase> blockDef = (Class<? extends IBase>) nextElementType;
|
||||||
def = new RuntimeChildResourceBlockDefinition(myContext, nextField, 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())
|
} else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType) || "Type".equals(nextElementType.getSimpleName())
|
||||||
|| IBaseDatatype.class.equals(nextElementType)) {
|
|| IBaseDatatype.class.equals(nextElementType)) {
|
||||||
|
|
||||||
def = new RuntimeChildAny(nextField, elementName, childAnnotation, descriptionAnnotation);
|
def = new RuntimeChildAny(nextField, elementName, childAnnotation, descriptionAnnotation);
|
||||||
} else if (IDatatype.class.isAssignableFrom(nextElementType) || IPrimitiveType.class.isAssignableFrom(nextElementType) || ICompositeType.class.isAssignableFrom(nextElementType)
|
} else if (IDatatype.class.isAssignableFrom(nextElementType) || IPrimitiveType.class.isAssignableFrom(nextElementType) || ICompositeType.class.isAssignableFrom(nextElementType)
|
||||||
|| IBaseDatatype.class.isAssignableFrom(nextElementType) || IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
|| IBaseDatatype.class.isAssignableFrom(nextElementType) || IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
||||||
Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
|
Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
|
||||||
|
|
||||||
if (IPrimitiveType.class.isAssignableFrom(nextElementType)) {
|
if (IPrimitiveType.class.isAssignableFrom(nextElementType)) {
|
||||||
|
@ -451,7 +428,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
def.setBindingValueSet(bindingAnnotation.valueSet());
|
def.setBindingValueSet(bindingAnnotation.valueSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def.setReplacedParentDefinition(replacedParent);
|
def.setReplacedParentDefinition(replacedParent);
|
||||||
|
@ -459,101 +436,104 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
elementNames.add(elementName);
|
elementNames.add(elementName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
|
@Override
|
||||||
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||||
if (mySealed) {
|
if (mySealed == SealingStateEnum.SEALED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mySealed = true;
|
|
||||||
|
|
||||||
scanCompositeElementForChildren();
|
synchronized (myContext) {
|
||||||
|
if (mySealed == SealingStateEnum.SEALED || mySealed == SealingStateEnum.SEALING) {
|
||||||
super.sealAndInitialize(theContext, theClassToElementDefinitions);
|
return;
|
||||||
|
|
||||||
for (BaseRuntimeChildDefinition next : myChildren) {
|
|
||||||
next.sealAndInitialize(theContext, theClassToElementDefinitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
myNameToChild = new HashMap<>();
|
|
||||||
for (BaseRuntimeChildDefinition next : myChildren) {
|
|
||||||
if (next instanceof RuntimeChildChoiceDefinition) {
|
|
||||||
String key = next.getElementName()+"[x]";
|
|
||||||
myNameToChild.put(key, next);
|
|
||||||
}
|
}
|
||||||
for (String nextName : next.getValidChildNames()) {
|
mySealed = SealingStateEnum.SEALING;
|
||||||
if (myNameToChild.containsKey(nextName)) {
|
|
||||||
throw new ConfigurationException("Duplicate child name[" + nextName + "] in Element[" + getName() + "]");
|
|
||||||
}
|
|
||||||
myNameToChild.put(nextName, next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
myChildren = Collections.unmodifiableList(myChildren);
|
scanCompositeElementForChildren();
|
||||||
myNameToChild = Collections.unmodifiableMap(myNameToChild);
|
|
||||||
|
super.sealAndInitialize(theContext, theClassToElementDefinitions);
|
||||||
List<BaseRuntimeChildDefinition> children = new ArrayList<>();
|
|
||||||
children.addAll(myChildren);
|
for (BaseRuntimeChildDefinition next : myChildren) {
|
||||||
|
next.sealAndInitialize(theContext, theClassToElementDefinitions);
|
||||||
/*
|
}
|
||||||
* Because of the way the type hierarchy works for DSTU2 resources,
|
|
||||||
* things end up in the wrong order
|
myNameToChild = new HashMap<>();
|
||||||
*/
|
for (BaseRuntimeChildDefinition next : myChildren) {
|
||||||
if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU2) {
|
if (next instanceof RuntimeChildChoiceDefinition) {
|
||||||
int extIndex = findIndex(children, "extension", false);
|
String key = next.getElementName() + "[x]";
|
||||||
int containedIndex = findIndex(children, "contained", false);
|
myNameToChild.put(key, next);
|
||||||
if (containedIndex != -1 && extIndex != -1 && extIndex < containedIndex) {
|
|
||||||
BaseRuntimeChildDefinition extension = children.remove(extIndex);
|
|
||||||
if (containedIndex > children.size()) {
|
|
||||||
children.add(extension);
|
|
||||||
} else {
|
|
||||||
children.add(containedIndex, extension);
|
|
||||||
}
|
}
|
||||||
int modIndex = findIndex(children, "modifierExtension", false);
|
for (String nextName : next.getValidChildNames()) {
|
||||||
if (modIndex < containedIndex) {
|
if (myNameToChild.containsKey(nextName)) {
|
||||||
extension = children.remove(modIndex);
|
throw new ConfigurationException("Duplicate child name[" + nextName + "] in Element[" + getName() + "]");
|
||||||
|
}
|
||||||
|
myNameToChild.put(nextName, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
myChildren = Collections.unmodifiableList(myChildren);
|
||||||
|
myNameToChild = Collections.unmodifiableMap(myNameToChild);
|
||||||
|
|
||||||
|
List<BaseRuntimeChildDefinition> children = new ArrayList<>();
|
||||||
|
children.addAll(myChildren);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Because of the way the type hierarchy works for DSTU2 resources,
|
||||||
|
* things end up in the wrong order
|
||||||
|
*/
|
||||||
|
if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU2) {
|
||||||
|
int extIndex = findIndex(children, "extension", false);
|
||||||
|
int containedIndex = findIndex(children, "contained", false);
|
||||||
|
if (containedIndex != -1 && extIndex != -1 && extIndex < containedIndex) {
|
||||||
|
BaseRuntimeChildDefinition extension = children.remove(extIndex);
|
||||||
if (containedIndex > children.size()) {
|
if (containedIndex > children.size()) {
|
||||||
children.add(extension);
|
children.add(extension);
|
||||||
} else {
|
} else {
|
||||||
children.add(containedIndex, extension);
|
children.add(containedIndex, extension);
|
||||||
}
|
}
|
||||||
|
int modIndex = findIndex(children, "modifierExtension", false);
|
||||||
|
if (modIndex < containedIndex) {
|
||||||
|
extension = children.remove(modIndex);
|
||||||
|
if (containedIndex > children.size()) {
|
||||||
|
children.add(extension);
|
||||||
|
} else {
|
||||||
|
children.add(containedIndex, extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add declared extensions alongside the undeclared ones
|
||||||
|
*/
|
||||||
|
if (getExtensionsNonModifier().isEmpty() == false) {
|
||||||
|
children.addAll(findIndex(children, "extension", true), getExtensionsNonModifier());
|
||||||
|
}
|
||||||
|
if (getExtensionsModifier().isEmpty() == false) {
|
||||||
|
children.addAll(findIndex(children, "modifierExtension", true), getExtensionsModifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
myChildrenAndExtensions = Collections.unmodifiableList(children);
|
||||||
|
mySealed = SealingStateEnum.SEALED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Add declared extensions alongside the undeclared ones
|
|
||||||
*/
|
|
||||||
if (getExtensionsNonModifier().isEmpty() == false) {
|
|
||||||
children.addAll(findIndex(children, "extension", true), getExtensionsNonModifier());
|
|
||||||
}
|
|
||||||
if (getExtensionsModifier().isEmpty() == false) {
|
|
||||||
children.addAll(findIndex(children, "modifierExtension", true), getExtensionsModifier());
|
|
||||||
}
|
|
||||||
|
|
||||||
myChildrenAndExtensions=Collections.unmodifiableList(children);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void validateSealed() {
|
protected void validateSealed() {
|
||||||
if (!mySealed) {
|
if (mySealed != SealingStateEnum.SEALED) {
|
||||||
synchronized(myContext) {
|
synchronized (myContext) {
|
||||||
if(!mySealed) {
|
if (mySealed == SealingStateEnum.NOT_SEALED) {
|
||||||
sealAndInitialize(myContext, myClassToElementDefinitions);
|
sealAndInitialize(myContext, myClassToElementDefinitions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int findIndex(List<BaseRuntimeChildDefinition> theChildren, String theName, boolean theDefaultAtEnd) {
|
private enum SealingStateEnum {
|
||||||
int index = theDefaultAtEnd ? theChildren.size() : -1;
|
NOT_SEALED,
|
||||||
for (ListIterator<BaseRuntimeChildDefinition> iter = theChildren.listIterator(); iter.hasNext(); ) {
|
SEALING,
|
||||||
if (iter.next().getElementName().equals(theName)) {
|
SEALED
|
||||||
index = iter.previousIndex();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ScannedField {
|
private static class ScannedField {
|
||||||
|
@ -563,6 +543,7 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
private Class<?> myElementType;
|
private Class<?> myElementType;
|
||||||
private Field myField;
|
private Field myField;
|
||||||
private boolean myFirstFieldInNewClass;
|
private boolean myFirstFieldInNewClass;
|
||||||
|
|
||||||
ScannedField(Field theField, Class<?> theClass, boolean theFirstFieldInNewClass) {
|
ScannedField(Field theField, Class<?> theClass, boolean theFirstFieldInNewClass) {
|
||||||
myField = theField;
|
myField = theField;
|
||||||
myFirstFieldInNewClass = theFirstFieldInNewClass;
|
myFirstFieldInNewClass = theFirstFieldInNewClass;
|
||||||
|
@ -576,13 +557,13 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
ourLog.trace("Ignoring constant {} on target type {}", theField.getName(), theClass);
|
ourLog.trace("Ignoring constant {} on target type {}", theField.getName(), theClass);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
myChildAnnotation = childAnnotation;
|
myChildAnnotation = childAnnotation;
|
||||||
myElementType = ModelScanner.determineElementType(theField);
|
myElementType = ModelScanner.determineElementType(theField);
|
||||||
|
|
||||||
Collections.addAll(myChoiceTypes, childAnnotation.type());
|
Collections.addAll(myChoiceTypes, childAnnotation.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Child getChildAnnotation() {
|
public Child getChildAnnotation() {
|
||||||
return myChildAnnotation;
|
return myChildAnnotation;
|
||||||
}
|
}
|
||||||
|
@ -609,4 +590,15 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int findIndex(List<BaseRuntimeChildDefinition> theChildren, String theName, boolean theDefaultAtEnd) {
|
||||||
|
int index = theDefaultAtEnd ? theChildren.size() : -1;
|
||||||
|
for (ListIterator<BaseRuntimeChildDefinition> iter = theChildren.listIterator(); iter.hasNext(); ) {
|
||||||
|
if (iter.next().getElementName().equals(theName)) {
|
||||||
|
index = iter.previousIndex();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,9 +224,8 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
||||||
/*
|
/*
|
||||||
* this does nothing, but BaseRuntimeElementCompositeDefinition
|
* this does nothing, but BaseRuntimeElementCompositeDefinition
|
||||||
* overrides this method to provide functionality because that class
|
* overrides this method to provide functionality because that class
|
||||||
* defers the dealing process
|
* defers the sealing process
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseRuntimeChildDefinition getChildByName(String theChildName) {
|
public BaseRuntimeChildDefinition getChildByName(String theChildName) {
|
||||||
|
|
|
@ -988,7 +988,8 @@ public class FhirTerser {
|
||||||
case RESOURCE_BLOCK:
|
case RESOURCE_BLOCK:
|
||||||
case COMPOSITE_DATATYPE: {
|
case COMPOSITE_DATATYPE: {
|
||||||
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def;
|
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def;
|
||||||
for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
|
List<BaseRuntimeChildDefinition> childrenAndExtensionDefs = childDef.getChildrenAndExtension();
|
||||||
|
for (BaseRuntimeChildDefinition nextChild : childrenAndExtensionDefs) {
|
||||||
|
|
||||||
List<?> values = nextChild.getAccessor().getValues(theElement);
|
List<?> values = nextChild.getAccessor().getValues(theElement);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 3046
|
||||||
|
title: When using deferred model scanning in highly parallelized environments, a crash could sometimes occur during parse/serialize operations.
|
|
@ -58,6 +58,8 @@ import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.CoreMatchers.either;
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
@ -228,7 +230,7 @@ public class InterceptorDstu3Test {
|
||||||
httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
||||||
HttpResponse status = ourClient.execute(httpPost);
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
try {
|
try {
|
||||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
assertThat(status.getStatusLine().getStatusCode(), either(equalTo(200)).or(equalTo(201)));
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.PerformanceOptionsEnum;
|
||||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.test.BaseTest;
|
import ca.uhn.fhir.test.BaseTest;
|
||||||
|
@ -43,9 +44,15 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
@ -270,6 +277,50 @@ public class JsonParserR4Test extends BaseTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure we can perform parallel parse/encodes against the same
|
||||||
|
* FhirContext instance when deferred model scanning is enabled without
|
||||||
|
* running into threading issues.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEncodeAndDecodeMultithreadedWithDeferredModelScanning() throws IOException, ExecutionException, InterruptedException {
|
||||||
|
String input = loadResource("/multi-thread-parsing-issue-bundle.json");
|
||||||
|
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(10);
|
||||||
|
for (int pass = 0; pass < 10; pass++) {
|
||||||
|
ourLog.info("Starting pass: {}", pass);
|
||||||
|
|
||||||
|
FhirContext parseCtx = FhirContext.forR4();
|
||||||
|
parseCtx.setPerformanceOptions(PerformanceOptionsEnum.DEFERRED_MODEL_SCANNING);
|
||||||
|
List<Future<Bundle>> bundleFutures = new ArrayList<>();
|
||||||
|
for (int readIdx = 0; readIdx < 10; readIdx++) {
|
||||||
|
bundleFutures.add(executor.submit(()->parseCtx.newJsonParser().parseResource(Bundle.class, input)));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Bundle> parsedBundles = new ArrayList<>();
|
||||||
|
for (Future<Bundle> nextFuture : bundleFutures) {
|
||||||
|
Bundle nextBundle = nextFuture.get();
|
||||||
|
parsedBundles.add(nextBundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
FhirContext encodeCtx = FhirContext.forR4();
|
||||||
|
encodeCtx.setPerformanceOptions(PerformanceOptionsEnum.DEFERRED_MODEL_SCANNING);
|
||||||
|
List<Future<String>> encodeFutures = new ArrayList<>();
|
||||||
|
for (Bundle nextBundle : parsedBundles) {
|
||||||
|
encodeFutures.add(executor.submit(()->encodeCtx.newJsonParser().encodeResourceToString(nextBundle)));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> outputs = new ArrayList<>();
|
||||||
|
for (Future<String> nextFuture : encodeFutures) {
|
||||||
|
outputs.add(nextFuture.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(outputs.get(0), outputs.get(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeAndParseUnicodeCharacterInNarrative() {
|
public void testEncodeAndParseUnicodeCharacterInNarrative() {
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
|
|
|
@ -0,0 +1,471 @@
|
||||||
|
{
|
||||||
|
"resourceType": "Bundle",
|
||||||
|
"type": "transaction",
|
||||||
|
"entry": [
|
||||||
|
{
|
||||||
|
"fullUrl": "5b6a6591-ae0f-3bfc-987e-5f144a43a036",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Condition",
|
||||||
|
"id": "5b6a6591-ae0f-3bfc-987e-5f144a43a036",
|
||||||
|
"meta": {
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"identifier": [
|
||||||
|
{
|
||||||
|
"system": "http://bluecrossnc.com/fhir/conditionIdentifier",
|
||||||
|
"value": "13028683"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"clinicalStatus": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
|
||||||
|
"code": "active"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"verificationStatus": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/condition-ver-status",
|
||||||
|
"code": "unconfirmed"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": [
|
||||||
|
{
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/condition-category",
|
||||||
|
"code": "encounter-diagnosis"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"code": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://hl7.org/fhir/sid/icd-10-cm",
|
||||||
|
"code": "Z80.3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"subject": {
|
||||||
|
"reference": "Patient?_type=http://terminology.hl7.org/CodeSystem/v2-0203|MB&identifier=111111111",
|
||||||
|
"identifier": {
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||||
|
"code": "MB"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"value": "111111111"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "Condition/5b6a6591-ae0f-3bfc-987e-5f144a43a036"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "a621019a-fadb-37dd-964a-01c21926e9b4",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Practitioner",
|
||||||
|
"id": "a621019a-fadb-37dd-964a-01c21926e9b4",
|
||||||
|
"meta": {
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner",
|
||||||
|
"http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Practitioner"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"identifier": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBIdentifierType",
|
||||||
|
"code": "npi"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"system": "http://hl7.org/fhir/sid/us-npi",
|
||||||
|
"value": "1134567787"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"active": true,
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"family": "BRIMMAGE",
|
||||||
|
"given": [
|
||||||
|
"BRAIN BRIMMAGE"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "Practitioner/a621019a-fadb-37dd-964a-01c21926e9b4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "fb1490ab-c62e-33ca-8414-b6437b0fa578",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Practitioner",
|
||||||
|
"id": "fb1490ab-c62e-33ca-8414-b6437b0fa578",
|
||||||
|
"meta": {
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner",
|
||||||
|
"http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Practitioner"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"identifier": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBIdentifierType",
|
||||||
|
"code": "npi"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"system": "http://hl7.org/fhir/sid/us-npi",
|
||||||
|
"value": "1386735827"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"active": true,
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"family": "NIEVES-ARRIBA",
|
||||||
|
"given": [
|
||||||
|
"NIEVES-ARRIBA, LUCYBETH"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "Practitioner/fb1490ab-c62e-33ca-8414-b6437b0fa578"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "77409d95-a9e3-3868-8706-069ee7074d90",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Location",
|
||||||
|
"id": "77409d95-a9e3-3868-8706-069ee7074d90",
|
||||||
|
"name": "LabCorp",
|
||||||
|
"address": {
|
||||||
|
"line": [
|
||||||
|
"231 MAPLE AVENUE",
|
||||||
|
"BURLINGTON"
|
||||||
|
],
|
||||||
|
"city": "NC",
|
||||||
|
"state": "27215"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "Location/77409d95-a9e3-3868-8706-069ee7074d90"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "6ee4bf07-3948-3093-b0ed-c84d0e511036",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Encounter",
|
||||||
|
"id": "6ee4bf07-3948-3093-b0ed-c84d0e511036",
|
||||||
|
"meta": {
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-encounter"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"identifier": [
|
||||||
|
{
|
||||||
|
"system": "http://bluecrossnc.com/fhir/encounterIdentifier",
|
||||||
|
"value": "13028683"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "finished",
|
||||||
|
"class": {
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
|
||||||
|
"code": "AMB"
|
||||||
|
},
|
||||||
|
"type": [
|
||||||
|
{
|
||||||
|
"text": "Authorization"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://www.ama-assn.org/go/cpt",
|
||||||
|
"code": "81163"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subject": {
|
||||||
|
"reference": "Patient?_type=http://terminology.hl7.org/CodeSystem/v2-0203|MB&identifier=111111111",
|
||||||
|
"identifier": {
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||||
|
"code": "MB"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"value": "111111111"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"participant": [
|
||||||
|
{
|
||||||
|
"type": [
|
||||||
|
{
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType",
|
||||||
|
"code": "PART"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"period": {
|
||||||
|
"start": "2021-08-27",
|
||||||
|
"end": "2021-09-26"
|
||||||
|
},
|
||||||
|
"individual": {
|
||||||
|
"reference": "Practitioner/a621019a-fadb-37dd-964a-01c21926e9b4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": [
|
||||||
|
{
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType",
|
||||||
|
"code": "PART"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"period": {
|
||||||
|
"start": "2019-04-15",
|
||||||
|
"end": "2019-05-15"
|
||||||
|
},
|
||||||
|
"individual": {
|
||||||
|
"reference": "Practitioner/fb1490ab-c62e-33ca-8414-b6437b0fa578"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"period": {
|
||||||
|
"start": "2019-04-15",
|
||||||
|
"end": "2019-05-15"
|
||||||
|
},
|
||||||
|
"reasonCode": [
|
||||||
|
{
|
||||||
|
"text": "Diagnostic Medical"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"location": [
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"reference": "Location/77409d95-a9e3-3868-8706-069ee7074d90"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "Encounter/6ee4bf07-3948-3093-b0ed-c84d0e511036"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "aa00fd16-222f-36e6-ad0d-df8cf9abb325",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "DocumentReference",
|
||||||
|
"id": "aa00fd16-222f-36e6-ad0d-df8cf9abb325",
|
||||||
|
"meta": {
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-documentreference"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"identifier": [
|
||||||
|
{
|
||||||
|
"system": "http://bluecrossnc.com/fhir/documentreferenceIdentifier",
|
||||||
|
"value": "127598"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "current",
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://loinc.org",
|
||||||
|
"code": "11506-3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": [
|
||||||
|
{
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category",
|
||||||
|
"code": "clinical-note"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subject": {
|
||||||
|
"reference": "Patient?_type=http://terminology.hl7.org/CodeSystem/v2-0203|MB&identifier=10215619000",
|
||||||
|
"identifier": {
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||||
|
"code": "MB"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"value": "10215619000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"date": "2021-09-16T20:00:00.000-04:00",
|
||||||
|
"author": [
|
||||||
|
{
|
||||||
|
"reference": "Organization/BCBSNC"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"attachment": {
|
||||||
|
"contentType": "text/plain",
|
||||||
|
"data": "VkdWemRDQlFhSGx6YVdOcFlXNGdVbUYwYVc5dVlXeGw="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"context": {
|
||||||
|
"encounter": [
|
||||||
|
{
|
||||||
|
"display": "13028683"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"period": {
|
||||||
|
"start": "2021-08-26T20:00:00-04:00",
|
||||||
|
"end": "2021-09-25T20:00:00-04:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "DocumentReference/aa00fd16-222f-36e6-ad0d-df8cf9abb325"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "89a97fee-729a-3953-9482-caeb82ee8db5",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Provenance",
|
||||||
|
"id": "89a97fee-729a-3953-9482-caeb82ee8db5",
|
||||||
|
"meta": {
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-provenance"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"target": [
|
||||||
|
{
|
||||||
|
"reference": "Patient?_type=http://terminology.hl7.org/CodeSystem/v2-0203|MB&identifier=10215619000",
|
||||||
|
"identifier": {
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||||
|
"code": "MB"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"value": "10215619000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "Condition/5b6a6591-ae0f-3bfc-987e-5f144a43a036"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "DocumentReference/aa00fd16-222f-36e6-ad0d-df8cf9abb325"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"recorded": "2021-08-27T00:00:00.000-04:00",
|
||||||
|
"agent": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type",
|
||||||
|
"code": "author"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"who": {
|
||||||
|
"reference": "Organization/BCBSNC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "Provenance/89a97fee-729a-3953-9482-caeb82ee8db5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fullUrl": "69d1f8f0-87e6-3cff-bd41-c3e01a5c479d",
|
||||||
|
"resource": {
|
||||||
|
"resourceType": "Provenance",
|
||||||
|
"id": "69d1f8f0-87e6-3cff-bd41-c3e01a5c479d",
|
||||||
|
"meta": {
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-provenance"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"target": [
|
||||||
|
{
|
||||||
|
"reference": "Patient?_type=http://terminology.hl7.org/CodeSystem/v2-0203|MB&identifier=10215619000",
|
||||||
|
"identifier": {
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||||
|
"code": "MB"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"value": "10215619000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "Encounter/6ee4bf07-3948-3093-b0ed-c84d0e511036"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"recorded": "2019-04-15T00:00:00.000-04:00",
|
||||||
|
"agent": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type",
|
||||||
|
"code": "author"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"who": {
|
||||||
|
"reference": "Organization/BCBSNC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"method": "PUT",
|
||||||
|
"url": "Provenance/69d1f8f0-87e6-3cff-bd41-c3e01a5c479d"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue