Work on cleanup

This commit is contained in:
James Agnew 2019-09-30 21:40:41 -04:00
parent b028337d8f
commit a1bf50685d
10 changed files with 305 additions and 308 deletions

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.context;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Map.Entry;
@ -68,8 +69,8 @@ public abstract class BaseRuntimeChildDefinition {
public interface IAccessor {
List<IBase> getValues(IBase theTarget);
default IBase getFirstValueOrNull(IBase theTarget) {
return getValues(theTarget).stream().findFirst().orElse(null);
default <T extends IBase> Optional<T> getFirstValueOrNull(IBase theTarget) {
return (Optional<T>) getValues(theTarget).stream().findFirst();
}
}

View File

@ -30,6 +30,7 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChildDefinition {
private final IAccessor myAccessor;
@ -184,8 +185,8 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
}
@Override
public IBase getFirstValueOrNull(IBase theTarget) {
return (IBase) getFieldValue(theTarget, myField);
public <T extends IBase> Optional<T> getFirstValueOrNull(IBase theTarget) {
return Optional.ofNullable(((T)getFieldValue(theTarget, myField)));
}
}

View File

@ -25,6 +25,7 @@ import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import javax.annotation.Nullable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@ -274,6 +275,7 @@ public class FhirContext {
* Note that this method is case insensitive!
* </p>
*/
@Nullable
public BaseRuntimeElementDefinition<?> getElementDefinition(final String theElementName) {
validateInitialized();
return myNameToElementDefinition.get(theElementName.toLowerCase());

View File

@ -79,19 +79,19 @@ class ParserState<T> {
}
}
public boolean elementIsRepeating(String theChildName) {
boolean elementIsRepeating(String theChildName) {
return myState.elementIsRepeating(theChildName);
}
public void endingElement() throws DataFormatException {
void endingElement() throws DataFormatException {
myState.endingElement();
}
public void enteringNewElement(String theNamespaceUri, String theName) throws DataFormatException {
void enteringNewElement(String theNamespaceUri, String theName) throws DataFormatException {
myState.enteringNewElement(theNamespaceUri, theName);
}
public void enteringNewElementExtension(StartElement theElem, String theUrlAttr, boolean theIsModifier, final String baseServerUrl) {
void enteringNewElementExtension(StartElement theElem, String theUrlAttr, boolean theIsModifier, final String baseServerUrl) {
myState.enteringNewElementExtension(theElem, theUrlAttr, theIsModifier, baseServerUrl);
}
@ -99,7 +99,7 @@ class ParserState<T> {
return myObject;
}
public boolean isPreResource() {
boolean isPreResource() {
return myState.isPreResource();
}
@ -135,18 +135,11 @@ class ParserState<T> {
myState.string(theData);
}
public boolean verifyNamespace(String theExpect, String theActual) {
if (myJsonMode) {
return true;
}
return StringUtils.equals(theExpect, theActual);
}
/**
* Invoked after any new XML event is individually processed, containing a copy of the XML event. This is basically
* intended for embedded XHTML content
*/
public void xmlEvent(XMLEvent theNextEvent) {
void xmlEvent(XMLEvent theNextEvent) {
if (myState != null) {
myState.xmlEvent(theNextEvent);
}
@ -157,7 +150,7 @@ class ParserState<T> {
private PreResourceState myPreResourceState;
private BaseState myStack;
public BaseState(PreResourceState thePreResourceState) {
BaseState(PreResourceState thePreResourceState) {
super();
myPreResourceState = thePreResourceState;
}
@ -223,7 +216,7 @@ class ParserState<T> {
return null;
}
public PreResourceState getPreResourceState() {
PreResourceState getPreResourceState() {
return myPreResourceState;
}
@ -231,7 +224,7 @@ class ParserState<T> {
return false;
}
protected void logAndSwallowUnexpectedElement(String theLocalPart) {
void logAndSwallowUnexpectedElement(String theLocalPart) {
myErrorHandler.unknownElement(null, theLocalPart);
push(new SwallowChildrenWholeState(getPreResourceState()));
}
@ -340,6 +333,7 @@ class ParserState<T> {
}
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
private class DeclaredExtensionState extends BaseState {
private IBase myChildInstance;
@ -431,10 +425,10 @@ class ParserState<T> {
private BaseRuntimeElementCompositeDefinition<?> myDefinition;
private IBase myInstance;
private Set<String> myParsedNonRepeatableNames = new HashSet<String>();
private Set<String> myParsedNonRepeatableNames = new HashSet<>();
private String myElementName;
public ElementCompositeState(PreResourceState thePreResourceState, String theElementName, BaseRuntimeElementCompositeDefinition<?> theDef, IBase theInstance) {
ElementCompositeState(PreResourceState thePreResourceState, String theElementName, BaseRuntimeElementCompositeDefinition<?> theDef, IBase theInstance) {
super(thePreResourceState);
myDefinition = theDef;
myInstance = theInstance;
@ -553,12 +547,9 @@ class ParserState<T> {
}
case CONTAINED_RESOURCES: {
List<? extends IBase> values = child.getAccessor().getValues(myInstance);
Object newDt;
if (values == null || values.isEmpty() || values.get(0) == null) {
newDt = newContainedDt((IResource) getPreResourceState().myInstance);
Object newDt = newContainedDt((IResource) getPreResourceState().myInstance);
child.getMutator().addValue(myInstance, (IBase) newDt);
} else {
newDt = values.get(0);
}
ContainedResourcesStateHapi state = new ContainedResourcesStateHapi(getPreResourceState());
push(state);
@ -611,7 +602,7 @@ class ParserState<T> {
private IBaseElement myElement;
public ElementIdState(ParserState<T>.PreResourceState thePreResourceState, IBaseElement theElement) {
ElementIdState(ParserState<T>.PreResourceState thePreResourceState, IBaseElement theElement) {
super(thePreResourceState);
myElement = theElement;
}
@ -632,7 +623,7 @@ class ParserState<T> {
private IBaseExtension<?, ?> myExtension;
public ExtensionState(PreResourceState thePreResourceState, IBaseExtension<?, ?> theExtension) {
ExtensionState(PreResourceState thePreResourceState, IBaseExtension<?, ?> theExtension) {
super(thePreResourceState);
myExtension = theExtension;
}
@ -712,7 +703,6 @@ class ParserState<T> {
// We hit an invalid type for the extension
myErrorHandler.unknownElement(null, theLocalPart);
push(new SwallowChildrenWholeState(getPreResourceState()));
return;
}
@Override
@ -758,50 +748,55 @@ class ParserState<T> {
@Override
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
if (theLocalPart.equals("versionId")) {
push(new MetaVersionElementState(getPreResourceState(), myMap));
// } else if (theLocalPart.equals("profile")) {
//
} else if (theLocalPart.equals("lastUpdated")) {
InstantDt updated = new InstantDt();
push(new PrimitiveState(getPreResourceState(), updated));
myMap.put(ResourceMetadataKeyEnum.UPDATED, updated);
} else if (theLocalPart.equals("security")) {
@SuppressWarnings("unchecked")
List<IBase> securityLabels = (List<IBase>) myMap.get(ResourceMetadataKeyEnum.SECURITY_LABELS);
if (securityLabels == null) {
securityLabels = new ArrayList<>();
myMap.put(ResourceMetadataKeyEnum.SECURITY_LABELS, securityLabels);
}
IBase securityLabel = myContext.getVersion().newCodingDt();
BaseRuntimeElementCompositeDefinition<?> codinfDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(securityLabel.getClass());
push(new SecurityLabelElementStateHapi(getPreResourceState(), codinfDef, securityLabel));
securityLabels.add(securityLabel);
} else if (theLocalPart.equals("profile")) {
@SuppressWarnings("unchecked")
List<IdDt> profiles = (List<IdDt>) myMap.get(ResourceMetadataKeyEnum.PROFILES);
List<IdDt> newProfiles;
if (profiles != null) {
newProfiles = new ArrayList<IdDt>(profiles.size() + 1);
newProfiles.addAll(profiles);
} else {
newProfiles = new ArrayList<IdDt>(1);
}
IdDt profile = new IdDt();
push(new PrimitiveState(getPreResourceState(), profile));
newProfiles.add(profile);
myMap.put(ResourceMetadataKeyEnum.PROFILES, Collections.unmodifiableList(newProfiles));
} else if (theLocalPart.equals("tag")) {
TagList tagList = (TagList) myMap.get(ResourceMetadataKeyEnum.TAG_LIST);
if (tagList == null) {
tagList = new TagList();
myMap.put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
}
push(new TagState(tagList));
} else {
myErrorHandler.unknownElement(null, theLocalPart);
push(new SwallowChildrenWholeState(getPreResourceState()));
return;
switch (theLocalPart) {
case "versionId":
push(new MetaVersionElementState(getPreResourceState(), myMap));
// } else if (theLocalPart.equals("profile")) {
//
break;
case "lastUpdated":
InstantDt updated = new InstantDt();
push(new PrimitiveState(getPreResourceState(), updated));
myMap.put(ResourceMetadataKeyEnum.UPDATED, updated);
break;
case "security":
@SuppressWarnings("unchecked")
List<IBase> securityLabels = (List<IBase>) myMap.get(ResourceMetadataKeyEnum.SECURITY_LABELS);
if (securityLabels == null) {
securityLabels = new ArrayList<>();
myMap.put(ResourceMetadataKeyEnum.SECURITY_LABELS, securityLabels);
}
IBase securityLabel = myContext.getVersion().newCodingDt();
BaseRuntimeElementCompositeDefinition<?> codinfDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(securityLabel.getClass());
push(new SecurityLabelElementStateHapi(getPreResourceState(), codinfDef, securityLabel));
securityLabels.add(securityLabel);
break;
case "profile":
@SuppressWarnings("unchecked")
List<IdDt> profiles = (List<IdDt>) myMap.get(ResourceMetadataKeyEnum.PROFILES);
List<IdDt> newProfiles;
if (profiles != null) {
newProfiles = new ArrayList<>(profiles.size() + 1);
newProfiles.addAll(profiles);
} else {
newProfiles = new ArrayList<>(1);
}
IdDt profile = new IdDt();
push(new PrimitiveState(getPreResourceState(), profile));
newProfiles.add(profile);
myMap.put(ResourceMetadataKeyEnum.PROFILES, Collections.unmodifiableList(newProfiles));
break;
case "tag":
TagList tagList = (TagList) myMap.get(ResourceMetadataKeyEnum.TAG_LIST);
if (tagList == null) {
tagList = new TagList();
myMap.put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
}
push(new TagState(tagList));
break;
default:
myErrorHandler.unknownElement(null, theLocalPart);
push(new SwallowChildrenWholeState(getPreResourceState()));
}
}
@ -830,7 +825,7 @@ class ParserState<T> {
private ResourceMetadataMap myMap;
public MetaVersionElementState(ParserState<T>.PreResourceState thePreResourceState, ResourceMetadataMap theMap) {
MetaVersionElementState(ParserState<T>.PreResourceState thePreResourceState, ResourceMetadataMap theMap) {
super(thePreResourceState);
myMap = theMap;
}
@ -849,7 +844,6 @@ class ParserState<T> {
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
myErrorHandler.unknownElement(null, theLocalPart);
push(new SwallowChildrenWholeState(getPreResourceState()));
return;
}
}
@ -859,13 +853,12 @@ class ParserState<T> {
private Map<String, IBaseResource> myContainedResources;
private IBaseResource myInstance;
private FhirVersionEnum myParentVersion;
private boolean myRequireResourceType = true;
private Class<? extends IBaseResource> myResourceType;
public PreResourceState(Class<? extends IBaseResource> theResourceType) {
PreResourceState(Class<? extends IBaseResource> theResourceType) {
super(null);
myResourceType = theResourceType;
myContainedResources = new HashMap<String, IBaseResource>();
myContainedResources = new HashMap<>();
if (theResourceType != null) {
myParentVersion = myContext.getResourceDefinition(theResourceType).getStructureVersion();
} else {
@ -873,7 +866,7 @@ class ParserState<T> {
}
}
public PreResourceState(PreResourceState thePreResourcesState, FhirVersionEnum theParentVersion) {
PreResourceState(PreResourceState thePreResourcesState, FhirVersionEnum theParentVersion) {
super(thePreResourcesState);
Validate.notNull(theParentVersion);
myParentVersion = theParentVersion;
@ -888,7 +881,7 @@ class ParserState<T> {
@Override
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
BaseRuntimeElementDefinition<?> definition;
RuntimeResourceDefinition definition;
if (myResourceType == null) {
definition = null;
if (myParser.getPreferTypes() != null) {
@ -908,17 +901,11 @@ class ParserState<T> {
} else {
definition = myContext.getResourceDefinition(myResourceType);
if (!StringUtils.equals(theLocalPart, definition.getName())) {
if (myRequireResourceType) {
throw new DataFormatException(myContext.getLocalizer().getMessage(ParserState.class, "wrongResourceTypeFound", definition.getName(), theLocalPart));
}
definition = myContext.getResourceDefinition(theLocalPart);
if (!(definition instanceof RuntimeResourceDefinition)) {
throw new DataFormatException("Element '" + theLocalPart + "' is not a resource, expected a resource at this position");
}
throw new DataFormatException(myContext.getLocalizer().getMessage(ParserState.class, "wrongResourceTypeFound", definition.getName(), theLocalPart));
}
}
RuntimeResourceDefinition def = (RuntimeResourceDefinition) definition;
RuntimeResourceDefinition def = definition;
if (!definition.getName().equals(theLocalPart) && definition.getName().equalsIgnoreCase(theLocalPart)) {
throw new DataFormatException("Unknown resource type '" + theLocalPart + "': Resource names are case sensitive, found similar name: '" + definition.getName() + "'");
}
@ -971,7 +958,7 @@ class ParserState<T> {
if (wantedProfileType != null && !wantedProfileType.equals(myInstance.getClass())) {
if (myResourceType == null || myResourceType.isAssignableFrom(wantedProfileType)) {
ourLog.debug("Converting resource of type {} to type defined for profile \"{}\": {}", new Object[]{myInstance.getClass().getName(), usedProfile, wantedProfileType});
ourLog.debug("Converting resource of type {} to type defined for profile \"{}\": {}", myInstance.getClass().getName(), usedProfile, wantedProfileType);
/*
* This isn't the most efficient thing really.. If we want a specific
@ -998,7 +985,7 @@ class ParserState<T> {
FhirTerser t = myContext.newTerser();
Map<String, IBaseResource> idToResource = new HashMap<String, IBaseResource>();
Map<String, IBaseResource> idToResource = new HashMap<>();
List<IBase> entries = t.getValues(myInstance, "Bundle.entry", IBase.class);
for (IBase nextEntry : entries) {
IPrimitiveType<?> fullUrl = t.getSingleValueOrNull(nextEntry, "fullUrl", IPrimitiveType.class);
@ -1051,7 +1038,7 @@ class ParserState<T> {
}
}
protected void weaveContainedResources() {
void weaveContainedResources() {
FhirTerser terser = myContext.newTerser();
terser.visit(myInstance, new IModelVisitor() {
@ -1103,12 +1090,12 @@ class ParserState<T> {
private IBase myTarget;
public PreResourceStateHapi(Class<? extends IBaseResource> theResourceType) {
PreResourceStateHapi(Class<? extends IBaseResource> theResourceType) {
super(theResourceType);
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
}
public PreResourceStateHapi(IBase theTarget, IMutator theMutator, Class<? extends IBaseResource> theResourceType) {
PreResourceStateHapi(IBase theTarget, IMutator theMutator, Class<? extends IBaseResource> theResourceType) {
super(theResourceType);
myTarget = theTarget;
myMutator = theMutator;
@ -1160,11 +1147,11 @@ class ParserState<T> {
private IMutator myMutator;
private IBase myTarget;
public PreResourceStateHl7Org(Class<? extends IBaseResource> theResourceType) {
PreResourceStateHl7Org(Class<? extends IBaseResource> theResourceType) {
super(theResourceType);
}
public PreResourceStateHl7Org(IBase theTarget, IMutator theMutator, Class<? extends IBaseResource> theResourceType) {
PreResourceStateHl7Org(IBase theTarget, IMutator theMutator, Class<? extends IBaseResource> theResourceType) {
super(theResourceType);
myMutator = theMutator;
myTarget = theTarget;
@ -1202,7 +1189,7 @@ class ParserState<T> {
private TagList myTagList;
public PreTagListState() {
PreTagListState() {
super(null);
myTagList = new TagList();
}
@ -1236,7 +1223,7 @@ class ParserState<T> {
private class PrimitiveState extends BaseState {
private IPrimitiveType<?> myInstance;
public PrimitiveState(PreResourceState thePreResourceState, IPrimitiveType<?> theInstance) {
PrimitiveState(PreResourceState thePreResourceState, IPrimitiveType<?> theInstance) {
super(thePreResourceState);
myInstance = theInstance;
}
@ -1288,7 +1275,6 @@ class ParserState<T> {
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
myErrorHandler.unknownElement(null, theLocalPart);
push(new SwallowChildrenWholeState(getPreResourceState()));
return;
}
@Override
@ -1321,7 +1307,7 @@ class ParserState<T> {
private class ResourceStateHl7Org extends ElementCompositeState {
public ResourceStateHl7Org(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IBaseResource theInstance) {
ResourceStateHl7Org(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IBaseResource theInstance) {
super(thePreResourceState, theDef.getName(), theDef, theInstance);
}
@ -1329,7 +1315,7 @@ class ParserState<T> {
private class SecurityLabelElementStateHapi extends ElementCompositeState {
public SecurityLabelElementStateHapi(ParserState<T>.PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IBase codingDt) {
SecurityLabelElementStateHapi(ParserState<T>.PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IBase codingDt) {
super(thePreResourceState, theDef.getName(), theDef, codingDt);
}
@ -1344,7 +1330,7 @@ class ParserState<T> {
private int myDepth;
public SwallowChildrenWholeState(PreResourceState thePreResourceState) {
SwallowChildrenWholeState(PreResourceState thePreResourceState) {
super(thePreResourceState);
}

View File

@ -24,21 +24,26 @@ import java.io.Serializable;
public interface IBaseEnumFactory<T extends Enum<?>> extends Serializable {
/**
* Read an enumeration value from the string that represents it on the XML or JSON
*
* @param codeString the value found in the XML or JSON
* @return the enumeration value
* @throws IllegalArgumentException is the value is not known
*/
public T fromCode(String codeString) throws IllegalArgumentException;
/**
* Read an enumeration value from the string that represents it on the XML or JSON
*
* @param codeString the value found in the XML or JSON
* @return the enumeration value
* @throws IllegalArgumentException is the value is not known
*/
T fromCode(String codeString) throws IllegalArgumentException;
/**
* Get the XML/JSON representation for an enumerated value
*
* @param code - the enumeration value
* @return the XML/JSON representation
*/
public String toCode(T code);
/**
* Get the XML/JSON representation for an enumerated value
*
* @param code - the enumeration value
* @return the XML/JSON representation
*/
String toCode(T code);
/**
* Get the system for a given enum value
*/
String toSystem(T theValue);
}

View File

@ -23,6 +23,6 @@ package org.hl7.fhir.instance.model.api;
public interface IBaseEnumeration<T extends Enum<?>> extends IPrimitiveType<T> {
// Marker interface
IBaseEnumFactory<T> getEnumFactory();
}

View File

@ -41,7 +41,7 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
/**
* @see ResourceEncodingEnum
*/
public static final int ENCODING_COL_LENGTH = 5;
static final int ENCODING_COL_LENGTH = 5;
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name = "SEQ_RESOURCE_HISTORY_ID", sequenceName = "SEQ_RESOURCE_HISTORY_ID")
@ -82,19 +82,6 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
return myProvenance;
}
public void setProvenance(ResourceHistoryProvenanceEntity theProvenance) {
myProvenance = theProvenance;
}
public void addTag(ResourceHistoryTag theTag) {
for (ResourceHistoryTag next : getTags()) {
if (next.equals(theTag)) {
return;
}
}
getTags().add(theTag);
}
public void addTag(ResourceTag theTag) {
ResourceHistoryTag tag = new ResourceHistoryTag(this, theTag.getTag());
tag.setResourceType(theTag.getResourceType());
@ -159,7 +146,7 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
@Override
public Collection<ResourceHistoryTag> getTags() {
if (myTags == null) {
myTags = new ArrayList<ResourceHistoryTag>();
myTags = new ArrayList<>();
}
return myTags;
}
@ -173,13 +160,4 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
myResourceVersion = theVersion;
}
public boolean hasTag(String theTerm, String theScheme) {
for (ResourceHistoryTag next : getTags()) {
if (next.getTag().getSystem().equals(theScheme) && next.getTag().getCode().equals(theTerm)) {
return true;
}
}
return false;
}
}

View File

@ -20,22 +20,25 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
* #L%
*/
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.ObjectUtils;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public abstract class BaseSearchParamExtractor implements ISearchParamExtractor {
public static final Pattern SPLIT = Pattern.compile("\\||( or )");
@ -47,6 +50,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private ISearchParamRegistry mySearchParamRegistry;
@Autowired
private ModelConfig myModelConfig;
private Set<Class<?>> myIgnoredForSearchDatatypes;
public BaseSearchParamExtractor() {
super();
@ -58,6 +62,10 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
mySearchParamRegistry = theSearchParamRegistry;
}
protected Set<Class<?>> getIgnoredForSearchDatatypes() {
return myIgnoredForSearchDatatypes;
}
@Override
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
List<PathAndRef> refs = new ArrayList<PathAndRef>();
@ -65,16 +73,43 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
for (String nextPath : nextPathsSplit) {
nextPath = nextPath.trim();
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null) {
continue;
if (nextObject != null) {
refs.add(new PathAndRef(nextPath, nextObject));
}
refs.add(new PathAndRef(nextPath, nextObject));
}
}
return refs;
}
protected abstract List<Object> extractValues(String thePaths, IBaseResource theResource);
/**
* Override parent because we're using FHIRPath here
*/
protected List<IBase> extractValues(String thePaths, IBaseResource theResource) {
List<IBase> values = new ArrayList<>();
if (isNotBlank(thePaths)) {
String[] nextPathsSplit = SPLIT_R4.split(thePaths);
for (String nextPath : nextPathsSplit) {
List<? extends IBase> allValues;
Supplier<List<? extends IBase>> allValuesFunc = getPathValueExtractor(theResource, nextPath);
allValues = allValuesFunc.get();
values.addAll(allValues);
}
for (int i = 0; i < values.size(); i++) {
IBase nextObject = values.get(i);
if (nextObject instanceof IBaseExtension) {
IBaseExtension nextExtension = (IBaseExtension) nextObject;
nextObject = nextExtension.getValue();
values.set(i, nextObject);
}
}
}
return values;
}
protected abstract Supplier<List<? extends IBase>> getPathValueExtractor(IBaseResource theResource, String theNextPath);
protected FhirContext getContext() {
return myContext;
@ -92,9 +127,25 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
return retVal;
}
@VisibleForTesting
void setContextForUnitTest(FhirContext theContext) {
myContext = theContext;
@PostConstruct
public void start() {
myIgnoredForSearchDatatypes = new HashSet<>();
addIgnoredType(getContext(), "Age", myIgnoredForSearchDatatypes);
addIgnoredType(getContext(), "Annotation", myIgnoredForSearchDatatypes);
addIgnoredType(getContext(), "Attachment", myIgnoredForSearchDatatypes);
addIgnoredType(getContext(), "Count", myIgnoredForSearchDatatypes);
addIgnoredType(getContext(), "Distance", myIgnoredForSearchDatatypes);
addIgnoredType(getContext(), "Ratio", myIgnoredForSearchDatatypes);
addIgnoredType(getContext(), "SampledData", myIgnoredForSearchDatatypes);
addIgnoredType(getContext(), "Signature", myIgnoredForSearchDatatypes);
addIgnoredType(getContext(), "LocationPositionComponent", myIgnoredForSearchDatatypes);
}
private static void addIgnoredType(FhirContext theCtx, String theType, Set<Class<?>> theIgnoredTypes) {
BaseRuntimeElementDefinition<?> elementDefinition = theCtx.getElementDefinition(theType);
if (elementDefinition != null) {
theIgnoredTypes.add(elementDefinition.getImplementingClass());
}
}

View File

@ -713,6 +713,7 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
myValidationSupport = theValidationSupport;
}
@Override
@PostConstruct
public void start() {
myWorkerContext = new HapiWorkerContext(getContext(), myValidationSupport);

View File

@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
* #L%
*/
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.model.entity.*;
@ -27,20 +29,18 @@ import ca.uhn.fhir.jpa.model.util.StringNormalizer;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestSecurityComponent;
import org.hl7.fhir.r5.model.Location.LocationPositionComponent;
import org.hl7.fhir.r5.model.Patient.PatientCommunicationComponent;
@ -52,6 +52,7 @@ import javax.measure.unit.NonSI;
import javax.measure.unit.Unit;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Supplier;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -59,22 +60,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements ISearchParamExtractor {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorR5.class);
private static final Set<Class<?>> ourIgnoredForSearchDatatypes;
static {
//noinspection unchecked
ourIgnoredForSearchDatatypes = Collections.unmodifiableSet(Sets.newHashSet(
Age.class,
Annotation.class,
Attachment.class,
Count.class,
Distance.class,
Ratio.class,
SampledData.class,
Signature.class,
LocationPositionComponent.class
));
}
@Autowired
private IValidationSupport myValidationSupport;
@ -87,33 +72,51 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
super();
}
@Override
@PostConstruct
public void initFhirPath() {
public void start() {
super.start();
IWorkerContext worker = new HapiWorkerContext(getContext(), myValidationSupport);
myFhirPathEngine = new FHIRPathEngine(worker);
myFhirPathEngine.setHostServices(new SearchParamExtractorR5HostServices());
}
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Quantity nextValue) {
if (!nextValue.getValueElement().isEmpty()) {
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
String nextValueString = nextValue.getSystemElement().getValueAsString();
String nextValueCode = nextValue.getCode();
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, IBase theQuantity) {
BaseRuntimeElementCompositeDefinition<?> quantityDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Quantity");
BaseRuntimeChildDefinition quantityValueChild = quantityDefinition.getChildByName("value");
BaseRuntimeChildDefinition quantitySystemChild = quantityDefinition.getChildByName("system");
BaseRuntimeChildDefinition quantityCodeChild = quantityDefinition.getChildByName("code");
Optional<IPrimitiveType<BigDecimal>> valueField = quantityValueChild.getAccessor().getFirstValueOrNull(theQuantity);
if (valueField.isPresent() && valueField.get().getValue() != null) {
BigDecimal nextValueValue = valueField.get().getValue();
String system = quantitySystemChild.getAccessor().<IPrimitiveType<String>>getFirstValueOrNull(theQuantity).map(t-> t.getValue()).orElse(null);
String code = quantityCodeChild.getAccessor().<IPrimitiveType<String>>getFirstValueOrNull(theQuantity).map(t-> t.getValue()).orElse(null);
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, system, code);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
private void addMoney(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Money nextValue) {
if (!nextValue.getValueElement().isEmpty()) {
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
private void addMoney(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, IBase theMoney) {
BaseRuntimeElementCompositeDefinition<?> moneyDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Money");
BaseRuntimeChildDefinition moneyValueChild = moneyDefinition.getChildByName("value");
BaseRuntimeChildDefinition moneyCurrencyChild = moneyDefinition.getChildByName("currency");
Optional<IPrimitiveType<BigDecimal>> valueField = moneyValueChild.getAccessor().getFirstValueOrNull(theMoney);
if (valueField.isPresent() && valueField.get().getValue() != null) {
BigDecimal nextValueValue = valueField.get().getValue();
String nextValueString = "urn:iso:std:iso:4217";
String nextValueCode = nextValue.getCurrency();
String nextValueCode = moneyCurrencyChild.getAccessor().<IPrimitiveType<String>>getFirstValueOrNull(theMoney).map(t-> t.getValue()).orElse(null);
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
@ -355,30 +358,24 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
for (IBase nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null || nextObject.isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
String typeName = getContext().getElementDefinition(nextObject.getClass()).getName();
if (nextObject instanceof Quantity) {
Quantity nextValue = (Quantity) nextObject;
addQuantity(theEntity, retVal, resourceName, nextValue);
} else if (nextObject instanceof Money) {
Money nextValue = (Money) nextObject;
addMoney(theEntity, retVal, resourceName, nextValue);
if (typeName.equals("Quantity")) {
addQuantity(theEntity, retVal, resourceName, nextObject);
} else if (typeName.equals("Money")) {
addMoney(theEntity, retVal, resourceName, nextObject);
} else if (nextObject instanceof Range) {
Range nextValue = (Range) nextObject;
addQuantity(theEntity, retVal, resourceName, nextValue.getLow());
addQuantity(theEntity, retVal, resourceName, nextValue.getHigh());
} else if (ourIgnoredForSearchDatatypes.contains(nextObject.getClass())) {
continue;
} else {
} else if (!getIgnoredForSearchDatatypes().contains(nextObject.getClass())) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
}
}
@ -545,8 +542,8 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
}
systems.add(nextValue.getSystemElement().getValueAsString());
codes.add(nextValue.getValueElement().getValue());
} else if (nextObject instanceof Enumeration<?>) {
Enumeration<?> obj = (Enumeration<?>) nextObject;
} else if (nextObject instanceof IBaseEnumeration<?>) {
IBaseEnumeration<?> obj = (IBaseEnumeration<?>) nextObject;
String system = extractSystem(obj);
String code = obj.getValueAsString();
if (isNotBlank(code)) {
@ -698,137 +695,112 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
}
}
/**
* Override parent because we're using FHIRPath here
*/
@Override
protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
IWorkerContext worker = new org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport);
FHIRPathEngine fp = new FHIRPathEngine(worker);
fp.setHostServices(new SearchParamExtractorR5HostServices());
List<Object> values = new ArrayList<>();
String[] nextPathsSplit = SPLIT_R4.split(thePaths);
for (String nextPath : nextPathsSplit) {
List<Base> allValues;
protected Supplier<List<? extends IBase>> getPathValueExtractor(IBaseResource theResource, String nextPath) {
return () -> {
try {
allValues = fp.evaluate((Base) theResource, nextPath);
IWorkerContext worker = new HapiWorkerContext(getContext(), myValidationSupport);
FHIRPathEngine fp = new FHIRPathEngine(worker);
fp.setHostServices(new SearchParamExtractorR5HostServices());
return fp.evaluate((Base) theResource, nextPath);
} catch (FHIRException e) {
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
throw new InternalErrorException(msg, e);
}
if (allValues.isEmpty() == false) {
values.addAll(allValues);
}
}
for (int i = 0; i < values.size(); i++) {
Object nextObject = values.get(i);
if (nextObject instanceof Extension) {
Extension nextExtension = (Extension) nextObject;
nextObject = nextExtension.getValue();
values.set(i, nextObject);
}
}
return values;
};
}
@VisibleForTesting
void setValidationSupportForTesting(IValidationSupport theValidationSupport) {
myValidationSupport = theValidationSupport;
private static class SearchParamExtractorR5HostServices implements FHIRPathEngine.IEvaluationContext {
private Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
@Override
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
return null;
}
private class SearchParamExtractorR5HostServices implements FHIRPathEngine.IEvaluationContext {
@Override
public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
return null;
}
private Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
@Override
public boolean log(String argument, List<Base> focus) {
return false;
}
@Override
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
return null;
}
@Override
public FunctionDetails resolveFunction(String functionName) {
return null;
}
@Override
public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
return null;
}
@Override
public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) throws PathEngineException {
return null;
}
@Override
public boolean log(String argument, List<Base> focus) {
return false;
}
@Override
public List<Base> executeFunction(Object appContext, String functionName, List<List<Base>> parameters) {
return null;
}
@Override
public FunctionDetails resolveFunction(String functionName) {
return null;
}
@Override
public Base resolveReference(Object theAppContext, String theUrl) throws FHIRException {
@Override
public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) throws PathEngineException {
return null;
}
/*
* When we're doing resolution within the SearchParamExtractor, if we want
* to do a resolve() it's just to check the type, so there is no point
* going through the heavyweight test. We can just return a stub and
* that's good enough since we're just doing something like
* Encounter.patient.where(resolve() is Patient)
*/
IdType url = new IdType(theUrl);
Base retVal = null;
if (isNotBlank(url.getResourceType())) {
@Override
public List<Base> executeFunction(Object appContext, String functionName, List<List<Base>> parameters) {
return null;
}
@Override
public Base resolveReference(Object theAppContext, String theUrl) throws FHIRException {
/*
* When we're doing resolution within the SearchParamExtractor, if we want
* to do a resolve() it's just to check the type, so there is no point
* going through the heavyweight test. We can just return a stub and
* that's good enough since we're just doing something like
* Encounter.patient.where(resolve() is Patient)
*/
IdType url = new IdType(theUrl);
Base retVal = null;
if (isNotBlank(url.getResourceType())) {
retVal = myResourceTypeToStub.get(url.getResourceType());
if (retVal != null) {
return retVal;
}
ResourceType resourceType = ResourceType.fromCode(url.getResourceType());
if (resourceType != null) {
retVal = new Resource() {
@Override
public Resource copy() {
return this;
}
@Override
public ResourceType getResourceType() {
return resourceType;
}
@Override
public String fhirType() {
return url.getResourceType();
}
};
myResourceTypeToStub.put(url.getResourceType(), retVal);
}
retVal = myResourceTypeToStub.get(url.getResourceType());
if (retVal != null) {
return retVal;
}
return retVal;
}
@Override
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
return false;
}
ResourceType resourceType = ResourceType.fromCode(url.getResourceType());
if (resourceType != null) {
retVal = new Resource() {
@Override
public Resource copy() {
return this;
}
@Override
public ValueSet resolveValueSet(Object theO, String theS) {
return null;
@Override
public ResourceType getResourceType() {
return resourceType;
}
@Override
public String fhirType() {
return url.getResourceType();
}
};
myResourceTypeToStub.put(url.getResourceType(), retVal);
}
}
return retVal;
}
private static <T extends Enum<?>> String extractSystem(Enumeration<T> theBoundCode) {
@Override
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
return false;
}
@Override
public ValueSet resolveValueSet(Object theO, String theS) {
return null;
}
}
private static <T extends Enum<?>> String extractSystem(IBaseEnumeration<T> theBoundCode) {
if (theBoundCode.getValue() != null) {
return theBoundCode.getEnumFactory().toSystem(theBoundCode.getValue());
}