Start working on compartment support

This commit is contained in:
James Agnew 2016-02-26 18:16:35 -05:00
parent d383b402d1
commit 39f6420066
20 changed files with 410 additions and 218 deletions

View File

@ -70,6 +70,8 @@ import ca.uhn.fhir.model.api.IResourceBlock;
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Compartment;
import ca.uhn.fhir.model.api.annotation.Compartments;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
@ -87,7 +89,6 @@ import ca.uhn.fhir.util.ReflectionUtil;
class ModelScanner {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelScanner.class);
private final Map<Class<? extends Annotation>, Class<? extends Annotation>> myAnnotationForwards = new HashMap<Class<? extends Annotation>, Class<? extends Annotation>>();
private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions = new HashMap<Class<? extends IBase>, BaseRuntimeElementDefinition<?>>();
private FhirContext myContext;
private Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = new HashMap<String, RuntimeResourceDefinition>();
@ -101,7 +102,8 @@ class ModelScanner {
private Set<Class<? extends IBase>> myVersionTypes;
ModelScanner(FhirContext theContext, FhirVersionEnum theVersion, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Collection<Class<? extends IElement>> theResourceTypes) throws ConfigurationException {
ModelScanner(FhirContext theContext, FhirVersionEnum theVersion, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingDefinitions,
Collection<Class<? extends IElement>> theResourceTypes) throws ConfigurationException {
myContext = theContext;
myVersion = theVersion;
Set<Class<? extends IBase>> toScan;
@ -233,68 +235,13 @@ class ModelScanner {
}
/**
* There are two implementations of all of the annotations (e.g. {@link Child} and
* {@link org.hl7.fhir.instance.model.annotations.Child}) since the HL7.org ones will eventually replace the HAPI
* ones. Annotations can't extend each other or implement interfaces or anything like that, so rather than duplicate
* all of the annotation processing code this method just creates an interface Proxy to simulate the HAPI
* annotations if the HL7.org ones are found instead.
* There are two implementations of all of the annotations (e.g. {@link Child} and {@link org.hl7.fhir.instance.model.annotations.Child}) since the HL7.org ones will eventually replace the HAPI
* ones. Annotations can't extend each other or implement interfaces or anything like that, so rather than duplicate all of the annotation processing code this method just creates an interface
* Proxy to simulate the HAPI annotations if the HL7.org ones are found instead.
*/
@SuppressWarnings("unchecked")
private <T extends Annotation> T pullAnnotation(Class<?> theContainer, AnnotatedElement theTarget, Class<T> theAnnotationType) {
private <T extends Annotation> T pullAnnotation(AnnotatedElement theTarget, Class<T> theAnnotationType) {
T retVal = theTarget.getAnnotation(theAnnotationType);
// if (myContext.getVersion().getVersion() != FhirVersionEnum.DSTU2_HL7ORG) {
return retVal;
// }
//
// if (retVal == null) {
// final Class<? extends Annotation> altAnnotationClass;
// /*
// * Use a cache to minimize Class.forName calls, since they are slow and expensive..
// */
// if (myAnnotationForwards.containsKey(theAnnotationType) == false) {
// String sourceClassName = theAnnotationType.getName();
// String candidateAltClassName = sourceClassName.replace("ca.uhn.fhir.model.api.annotation", "org.hl7.fhir.instance.model.annotations");
// if (!sourceClassName.equals(candidateAltClassName)) {
// Class<?> forName;
// try {
// forName = Class.forName(candidateAltClassName);
// ourLog.debug("Forwarding annotation request for [{}] to class [{}]", theAnnotationType, forName);
// } catch (ClassNotFoundException e) {
// forName = null;
// }
// altAnnotationClass = (Class<? extends Annotation>) forName;
// } else {
// altAnnotationClass = null;
// }
// myAnnotationForwards.put(theAnnotationType, altAnnotationClass);
// } else {
// altAnnotationClass = myAnnotationForwards.get(theAnnotationType);
// }
//
// if (altAnnotationClass == null) {
// return null;
// }
//
// final Annotation altAnnotation;
// altAnnotation = theTarget.getAnnotation(altAnnotationClass);
// if (altAnnotation == null) {
// return null;
// }
//
// InvocationHandler h = new InvocationHandler() {
//
// @Override
// public Object invoke(Object theProxy, Method theMethod, Object[] theArgs) throws Throwable {
// Method altMethod = altAnnotationClass.getMethod(theMethod.getName(), theMethod.getParameterTypes());
// return altMethod.invoke(altAnnotation, theArgs);
// }
// };
// retVal = (T) Proxy.newProxyInstance(theAnnotationType.getClassLoader(), new Class<?>[] { theAnnotationType }, h);
//
// }
//
// return retVal;
}
private void scan(Class<? extends IBase> theClass) throws ConfigurationException {
@ -303,17 +250,18 @@ class ModelScanner {
return;
}
ResourceDef resourceDefinition = pullAnnotation(theClass, theClass, ResourceDef.class);
ResourceDef resourceDefinition = pullAnnotation(theClass, ResourceDef.class);
if (resourceDefinition != null) {
if (!IBaseResource.class.isAssignableFrom(theClass)) {
throw new ConfigurationException("Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": " + theClass.getCanonicalName());
throw new ConfigurationException(
"Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": " + theClass.getCanonicalName());
}
@SuppressWarnings("unchecked")
Class<? extends IBaseResource> resClass = (Class<? extends IBaseResource>) theClass;
scanResource(resClass, resourceDefinition);
}
DatatypeDef datatypeDefinition = pullAnnotation(theClass, theClass, DatatypeDef.class);
DatatypeDef datatypeDefinition = pullAnnotation(theClass, DatatypeDef.class);
if (datatypeDefinition != null) {
if (ICompositeType.class.isAssignableFrom(theClass)) {
@SuppressWarnings("unchecked")
@ -325,17 +273,19 @@ class ModelScanner {
scanPrimitiveDatatype(resClass, datatypeDefinition);
} else {
return;
// throw new ConfigurationException("Resource type contains a @" + DatatypeDef.class.getSimpleName() + " annotation but does not implement " + IDatatype.class.getCanonicalName() + ": " + theClass.getCanonicalName());
// throw new ConfigurationException("Resource type contains a @" + DatatypeDef.class.getSimpleName() + " annotation but does not implement " + IDatatype.class.getCanonicalName() + ": " +
// theClass.getCanonicalName());
}
}
Block blockDefinition = pullAnnotation(theClass, theClass, Block.class);
Block blockDefinition = pullAnnotation(theClass, Block.class);
if (blockDefinition != null) {
if (IResourceBlock.class.isAssignableFrom(theClass) || IBaseBackboneElement.class.isAssignableFrom(theClass) || IBaseDatatypeElement.class.isAssignableFrom(theClass)) {
scanBlock(theClass);
} else {
throw new ConfigurationException("Type contains a @" + Block.class.getSimpleName() + " annotation but does not implement " + IResourceBlock.class.getCanonicalName() + ": " + theClass.getCanonicalName());
throw new ConfigurationException(
"Type contains a @" + Block.class.getSimpleName() + " annotation but does not implement " + IResourceBlock.class.getCanonicalName() + ": " + theClass.getCanonicalName());
}
}
@ -429,7 +379,8 @@ class ModelScanner {
}
@SuppressWarnings("unchecked")
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
int baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
for (Field next : theClass.getDeclaredFields()) {
@ -439,16 +390,16 @@ class ModelScanner {
continue;
}
Child childAnnotation = pullAnnotation(theClass, next, Child.class);
Child childAnnotation = pullAnnotation(next, Child.class);
if (childAnnotation == null) {
ourLog.trace("Ignoring non @Child field {} on target type {}", next.getName(), theClass);
continue;
}
Description descriptionAnnotation = pullAnnotation(theClass, next, Description.class);
Description descriptionAnnotation = pullAnnotation(next, Description.class);
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
Extension extensionAttr = pullAnnotation(theClass, next, Extension.class);
Extension extensionAttr = pullAnnotation(next, Extension.class);
if (extensionAttr != null) {
orderMap = theOrderToExtensionDef;
}
@ -471,8 +422,8 @@ class ModelScanner {
}
}
if (order == Child.REPLACE_PARENT) {
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT + ") but no parent element with extension URL " + extensionAttr.url() + " could be found on type "
+ next.getDeclaringClass().getSimpleName());
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
+ ") but no parent element with extension URL " + extensionAttr.url() + " could be found on type " + next.getDeclaringClass().getSimpleName());
}
} else {
@ -487,8 +438,8 @@ class ModelScanner {
}
}
if (order == Child.REPLACE_PARENT) {
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT + ") but no parent element with name " + elementName + " could be found on type "
+ next.getDeclaringClass().getSimpleName());
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
+ ") but no parent element with name " + elementName + " could be found on type " + next.getDeclaringClass().getSimpleName());
}
}
@ -504,8 +455,7 @@ class ModelScanner {
// int max = childAnnotation.max();
/*
* Anything that's marked as unknown is given a new ID that is <0 so that it doesn't conflict with any given
* IDs and can be figured out later
* Anything that's marked as unknown is given a new ID that is <0 so that it doesn't conflict with any given IDs and can be figured out later
*/
if (order == Child.ORDER_UNKNOWN) {
order = Integer.MIN_VALUE;
@ -572,7 +522,8 @@ class ModelScanner {
binder = getBoundCodeBinder(next);
}
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et, binder);
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et,
binder);
if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
def.setEnumerationType(ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next));
@ -589,7 +540,8 @@ class ModelScanner {
List<Class<? extends IBaseResource>> refTypesList = new ArrayList<Class<? extends IBaseResource>>();
for (Class<? extends IElement> nextType : childAnnotation.type()) {
if (IBaseResource.class.isAssignableFrom(nextType) == false) {
throw new ConfigurationException("Field '" + next.getName() + "' in class '" + next.getDeclaringClass().getCanonicalName() + "' is of type " + BaseResourceReferenceDt.class + " but contains a non-resource type: " + nextType.getCanonicalName());
throw new ConfigurationException("Field '" + next.getName() + "' in class '" + next.getDeclaringClass().getCanonicalName() + "' is of type " + BaseResourceReferenceDt.class
+ " but contains a non-resource type: " + nextType.getCanonicalName());
}
refTypesList.add((Class<? extends IBaseResource>) nextType);
addScanAlso(nextType);
@ -597,10 +549,10 @@ class ModelScanner {
RuntimeChildResourceDefinition def = new RuntimeChildResourceDefinition(next, elementName, childAnnotation, descriptionAnnotation, refTypesList);
orderMap.put(order, def);
} else if (IResourceBlock.class.isAssignableFrom(nextElementType) || IBaseBackboneElement.class.isAssignableFrom(nextElementType) || IBaseDatatypeElement.class.isAssignableFrom(nextElementType)) {
} else if (IResourceBlock.class.isAssignableFrom(nextElementType) || IBaseBackboneElement.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?
*/
Class<? extends IBase> blockDef = (Class<? extends IBase>) nextElementType;
@ -608,13 +560,14 @@ class ModelScanner {
RuntimeChildResourceBlockDefinition def = new RuntimeChildResourceBlockDefinition(next, childAnnotation, descriptionAnnotation, elementName, blockDef);
orderMap.put(order, def);
} else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType) || "Type".equals(nextElementType.getSimpleName()) || IBaseDatatype.class.equals(nextElementType)) {
} else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType) || "Type".equals(nextElementType.getSimpleName())
|| IBaseDatatype.class.equals(nextElementType)) {
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
orderMap.put(order, def);
} else if (IDatatype.class.isAssignableFrom(nextElementType) || IPrimitiveType.class.isAssignableFrom(nextElementType) || ICompositeType.class.isAssignableFrom(nextElementType) || IBaseDatatype.class.isAssignableFrom(nextElementType)
|| IBaseExtension.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)) {
Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
addScanAlso(nextDatatype);
@ -642,10 +595,11 @@ class ModelScanner {
}
}
CodeableConceptElement concept = pullAnnotation(theClass, next, CodeableConceptElement.class);
CodeableConceptElement concept = pullAnnotation(next, CodeableConceptElement.class);
if (concept != null) {
if (!ICodedDatatype.class.isAssignableFrom(nextDatatype)) {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is marked as @" + CodeableConceptElement.class.getCanonicalName() + " but type is not a subtype of " + ICodedDatatype.class.getName());
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is marked as @" + CodeableConceptElement.class.getCanonicalName()
+ " but type is not a subtype of " + ICodedDatatype.class.getName());
} else {
Class<? extends ICodeEnum> type = concept.type();
myScanAlsoCodeTable.add(type);
@ -708,21 +662,23 @@ class ModelScanner {
Class<?> parent = theClass.getSuperclass();
primaryNameProvider = false;
while (parent.equals(Object.class) == false && isBlank(resourceName)) {
ResourceDef nextDef = pullAnnotation(theClass, parent, ResourceDef.class);
ResourceDef nextDef = pullAnnotation(parent, ResourceDef.class);
if (nextDef != null) {
resourceName = nextDef.name();
}
parent = parent.getSuperclass();
}
if (isBlank(resourceName)) {
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name(): " + theClass.getCanonicalName() + " - This is only allowed for types that extend other resource types ");
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name(): " + theClass.getCanonicalName()
+ " - This is only allowed for types that extend other resource types ");
}
}
String resourceId = resourceDefinition.id();
if (!isBlank(resourceId)) {
if (myIdToResourceDefinition.containsKey(resourceId)) {
throw new ConfigurationException("The following resource types have the same ID of '" + resourceId + "' - " + theClass.getCanonicalName() + " and " + myIdToResourceDefinition.get(resourceId).getImplementingClass().getCanonicalName());
throw new ConfigurationException("The following resource types have the same ID of '" + resourceId + "' - " + theClass.getCanonicalName() + " and "
+ myIdToResourceDefinition.get(resourceId).getImplementingClass().getCanonicalName());
}
}
@ -748,7 +704,7 @@ class ModelScanner {
Map<Field, SearchParamDefinition> compositeFields = new LinkedHashMap<Field, SearchParamDefinition>();
for (Field nextField : theClass.getFields()) {
SearchParamDefinition searchParam = pullAnnotation(theClass, nextField, SearchParamDefinition.class);
SearchParamDefinition searchParam = pullAnnotation(nextField, SearchParamDefinition.class);
if (searchParam != null) {
RestSearchParameterTypeEnum paramType = RestSearchParameterTypeEnum.valueOf(searchParam.type().toUpperCase());
if (paramType == null) {
@ -758,7 +714,17 @@ class ModelScanner {
compositeFields.put(nextField, searchParam);
continue;
}
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType);
Set<String> providesMembershipInCompartments = null;
Compartments compartmentsAnnotation = pullAnnotation(nextField, Compartments.class);
if (compartmentsAnnotation != null) {
providesMembershipInCompartments = new HashSet<String>();
for (Compartment next : compartmentsAnnotation.providesMembershipIn()) {
providesMembershipInCompartments.add(next.name());
}
}
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType, providesMembershipInCompartments);
theResourceDef.addSearchParam(param);
nameToParam.put(param.getName(), param);
}
@ -771,13 +737,14 @@ class ModelScanner {
for (String nextName : searchParam.compositeOf()) {
RuntimeSearchParam param = nameToParam.get(nextName);
if (param == null) {
ourLog.warn("Search parameter {}.{} declares that it is a composite with compositeOf value '{}' but that is not a valid parametr name itself. Valid values are: {}", new Object[] { theResourceDef.getName(), searchParam.name(), nextName, nameToParam.keySet() });
ourLog.warn("Search parameter {}.{} declares that it is a composite with compositeOf value '{}' but that is not a valid parametr name itself. Valid values are: {}",
new Object[] { theResourceDef.getName(), searchParam.name(), nextName, nameToParam.keySet() });
continue;
}
compositeOf.add(param);
}
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), RestSearchParameterTypeEnum.COMPOSITE, compositeOf);
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), RestSearchParameterTypeEnum.COMPOSITE, compositeOf, null);
theResourceDef.addSearchParam(param);
}
}

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.context;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
@ -29,23 +30,30 @@ import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
public class RuntimeSearchParam {
private List<RuntimeSearchParam> myCompositeOf;
private String myDescription;
private String myName;
private RestSearchParameterTypeEnum myParamType;
private String myPath;
private List<RuntimeSearchParam> myCompositeOf;
private Set<String> myProvidesMembershipInCompartments;
public RuntimeSearchParam(String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType) {
this(theName, theDescription, thePath, theParamType, null);
}
public RuntimeSearchParam(String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, List<RuntimeSearchParam> theCompositeOf) {
public RuntimeSearchParam(String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, List<RuntimeSearchParam> theCompositeOf,
Set<String> theProvidesMembershipInCompartments) {
super();
myName = theName;
myDescription = theDescription;
myPath = thePath;
myParamType = theParamType;
myCompositeOf = theCompositeOf;
if (theProvidesMembershipInCompartments != null && !theProvidesMembershipInCompartments.isEmpty()) {
myProvidesMembershipInCompartments = Collections.unmodifiableSet(theProvidesMembershipInCompartments);
} else {
myProvidesMembershipInCompartments = null;
}
}
public RuntimeSearchParam(String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set<String> theProvidesMembershipInCompartments) {
this(theName, theDescription, thePath, theParamType, null, theProvidesMembershipInCompartments);
}
public List<RuntimeSearchParam> getCompositeOf() {
@ -83,4 +91,11 @@ public class RuntimeSearchParam {
return retVal;
}
/**
* Can return null
*/
public Set<String> getProvidesMembershipInCompartments() {
return myProvidesMembershipInCompartments;
}
}

View File

@ -0,0 +1,19 @@
package ca.uhn.fhir.model.api.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(value= {})
public @interface Compartment {
/**
* This may only be populated on a reference field. On such a field, places the containing
* resource in a compartment with the name(s) specified by the given strings, where the compartment
* belongs to the target resource. For example, this field could be populated with <code>Patient</code> on
* the <code>Observation.subject</code> field.
*/
String name();
}

View File

@ -0,0 +1,14 @@
package ca.uhn.fhir.model.api.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(value= {ElementType.FIELD})
public @interface Compartments {
Compartment[] providesMembershipIn();
}

View File

@ -39,53 +39,34 @@ public class DaoConfig {
// ***
private boolean myAllowInlineMatchUrlReferences = false;
/**
* @see #setAllowInlineMatchUrlReferences(boolean)
*/
public boolean isAllowInlineMatchUrlReferences() {
return myAllowInlineMatchUrlReferences;
}
/**
* Should references containing match URLs be resolved and replaced in create and update operations. For
* example, if this property is set to true and a resource is created containing a reference
* to "Patient?identifier=12345", this is reference match URL will be resolved and replaced according
* to the usual match URL rules.
* <p>
* Default is false for now, as this is an experimental feature.
* </p>
* @since 1.5
*/
public void setAllowInlineMatchUrlReferences(boolean theAllowInlineMatchUrlReferences) {
myAllowInlineMatchUrlReferences = theAllowInlineMatchUrlReferences;
}
private boolean myAllowMultipleDelete;
private int myHardSearchLimit = 1000;
private int myHardTagListLimit = 1000;
private int myIncludeLimit = 2000;
// ***
// update setter javadoc if default changes
// ***
private boolean myIndexContainedResources = true;
private List<IServerInterceptor> myInterceptors;
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
private boolean mySchedulingDisabled;
private boolean mySubscriptionEnabled;
private long mySubscriptionPollDelay = 1000;
private Long mySubscriptionPurgeInactiveAfterMillis;
/**
* See {@link #setIncludeLimit(int)}
*/
public int getHardSearchLimit() {
return myHardSearchLimit;
}
public int getHardTagListLimit() {
return myHardTagListLimit;
}
public int getIncludeLimit() {
return myIncludeLimit;
}
/**
* Returns the interceptors which will be notified of operations.
*
@ -110,10 +91,25 @@ public class DaoConfig {
return mySubscriptionPurgeInactiveAfterMillis;
}
/**
* @see #setAllowInlineMatchUrlReferences(boolean)
*/
public boolean isAllowInlineMatchUrlReferences() {
return myAllowInlineMatchUrlReferences;
}
public boolean isAllowMultipleDelete() {
return myAllowMultipleDelete;
}
/**
* Should contained IDs be indexed the same way that non-contained IDs are (default is
* <code>true</code>)
*/
public boolean isIndexContainedResources() {
return myIndexContainedResources;
}
public boolean isSchedulingDisabled() {
return mySchedulingDisabled;
}
@ -125,6 +121,20 @@ public class DaoConfig {
return mySubscriptionEnabled;
}
/**
* Should references containing match URLs be resolved and replaced in create and update operations. For
* example, if this property is set to true and a resource is created containing a reference
* to "Patient?identifier=12345", this is reference match URL will be resolved and replaced according
* to the usual match URL rules.
* <p>
* Default is false for now, as this is an experimental feature.
* </p>
* @since 1.5
*/
public void setAllowInlineMatchUrlReferences(boolean theAllowInlineMatchUrlReferences) {
myAllowInlineMatchUrlReferences = theAllowInlineMatchUrlReferences;
}
public void setAllowMultipleDelete(boolean theAllowMultipleDelete) {
myAllowMultipleDelete = theAllowMultipleDelete;
}
@ -146,6 +156,14 @@ public class DaoConfig {
myIncludeLimit = theIncludeLimit;
}
/**
* Should contained IDs be indexed the same way that non-contained IDs are (default is
* <code>true</code>)
*/
public void setIndexContainedResources(boolean theIndexContainedResources) {
myIndexContainedResources = theIndexContainedResources;
}
/**
* This may be used to optionally register server interceptors directly against the DAOs.
* <p>

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.entity;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
import org.apache.commons.lang3.builder.EqualsBuilder;
@ -34,9 +35,8 @@ import org.hibernate.search.annotations.Field;
//@formatter:off
@Embeddable
@Entity
@Table(name = "HFJ_SPIDX_COORDS" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */)
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_COORDS", indexes = {
@org.hibernate.annotations.Index(name = "IDX_SP_COORDS", columnNames = { "RES_TYPE", "SP_NAME", "SP_LATITUDE" })
@Table(name = "HFJ_SPIDX_COORDS", indexes = {
@Index(name = "IDX_SP_COORDS", columnList = "RES_TYPE,SP_NAME,SP_LATITUDE,SP_LONGITUDE")
})
//@formatter:on
public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchParam {

View File

@ -144,6 +144,9 @@ public class ResourceTable extends BaseHasResource implements Serializable {
//@formatter:on
private String myContentText;
@Column(name = "HAS_CONTAINED", nullable = true)
private boolean myHasContainedResource;
@Column(name = "SP_HAS_LINKS")
private boolean myHasLinks;
@ -158,6 +161,9 @@ public class ResourceTable extends BaseHasResource implements Serializable {
@Column(name = "SP_INDEX_STATUS", nullable = true)
private Long myIndexStatus;
@Column(name = "IS_CONTAINED", nullable = true)
private boolean myIsContainedResource;
@Column(name = "RES_LANGUAGE", length = MAX_LANGUAGE_LENGTH, nullable = true)
private String myLanguage;
@ -340,10 +346,18 @@ public class ResourceTable extends BaseHasResource implements Serializable {
return false;
}
public boolean isHasContainedResource() {
return myHasContainedResource;
}
public boolean isHasLinks() {
return myHasLinks;
}
public boolean isIsContainedResource() {
return myIsContainedResource;
}
public boolean isParamsCoordsPopulated() {
return myParamsCoordsPopulated;
}
@ -376,6 +390,10 @@ public class ResourceTable extends BaseHasResource implements Serializable {
myContentText = theContentText;
}
public void setHasContainedResource(boolean theHasContainedResource) {
myHasContainedResource = theHasContainedResource;
}
public void setHasLinks(boolean theHasLinks) {
myHasLinks = theHasLinks;
}
@ -388,6 +406,10 @@ public class ResourceTable extends BaseHasResource implements Serializable {
myIndexStatus = theIndexStatus;
}
public void setIsContainedResource(boolean theIsContainedResource) {
myIsContainedResource = theIsContainedResource;
}
public void setLanguage(String theLanguage) {
if (defaultString(theLanguage).length() > MAX_LANGUAGE_LENGTH) {
throw new UnprocessableEntityException("Language exceeds maximum length of " + MAX_LANGUAGE_LENGTH + " chars: " + theLanguage);

View File

@ -46,6 +46,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
@ -225,14 +226,14 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
public TransactionTemplate newTxTemplate() {
TransactionTemplate retVal = new TransactionTemplate(myTxManager);
retVal.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
retVal.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
retVal.afterPropertiesSet();
return retVal;
}
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager) {
TransactionTemplate txTemplate = new TransactionTemplate(theTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
txTemplate.execute(new TransactionCallback<Void>() {
@Override
public Void doInTransaction(TransactionStatus theStatus) {

View File

@ -0,0 +1,58 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.Test;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ContainedTest.class);
@Test
public void before() {
myDaoConfig.setIndexContainedResources(true);
}
@Test
public void testIndexContained() {
Patient p = new Patient();
p.setId("#some_patient");
p.addName().addFamily("MYFAMILY").addGiven("MYGIVEN");
Observation o1 = new Observation();
o1.getCode().setText("Some Observation");
o1.setSubject(new Reference(p));
IIdType oid1 = myObservationDao.create(o1).getId().toUnqualifiedVersionless();
Observation o2 = new Observation();
o2.getCode().setText("Some Observation");
o2.setSubject(new Reference(p));
IIdType oid2 = myObservationDao.create(o2).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().addFamily("MYFAMILY").addGiven("MYGIVEN");
IIdType pid2 = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(o2));
SearchParameterMap map;
// map = new SearchParameterMap();
// map.add(Observation.SP_CODE, new TokenParam(null, "some observation").setModifier(TokenParamModifier.TEXT));
// assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2)));
}
// TODO: make sure match URLs don't delete
}

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.rest.server.Constants;
public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
@ -56,15 +57,15 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
SearchParameterMap map;
map = new SearchParameterMap();
map.add(Observation.SP_CODE, new TokenParam(null, "blood").setText(true));
map.add(Observation.SP_CODE, new TokenParam(null, "blood").setModifier(TokenParamModifier.TEXT));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2)));
map = new SearchParameterMap();
map.add(Observation.SP_CODE, new TokenParam(null, "blood").setText(true));
map.add(Observation.SP_CODE, new TokenParam(null, "blood").setModifier(TokenParamModifier.TEXT));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
map = new SearchParameterMap();
map.add(Observation.SP_CODE, new TokenParam(null, "blood").setText(true));
map.add(Observation.SP_CODE, new TokenParam(null, "blood").setModifier(TokenParamModifier.TEXT));
map.add(Constants.PARAM_CONTENT, new StringParam("obs1"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1)));
@ -358,7 +359,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
Device dev2 = new Device();
dev2.setManufacturer("Some Manufacturer 2");
IIdType devId2 = myDeviceDao.create(dev2).getId().toUnqualifiedVersionless();
myDeviceDao.create(dev2).getId().toUnqualifiedVersionless();
Observation obs1 = new Observation();
obs1.getSubject().setReferenceElement(ptId1);

View File

@ -23,9 +23,9 @@ import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.model.BaseResource;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.Device;
@ -46,12 +46,12 @@ import org.hl7.fhir.dstu3.model.Quantity;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.Subscription;
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType;
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
import org.hl7.fhir.dstu3.model.Substance;
import org.hl7.fhir.dstu3.model.TemporalPrecisionEnum;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType;
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.Ignore;
@ -68,7 +68,6 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.CompositeParam;
@ -585,21 +584,21 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
params.put(BaseResource.SP_RES_LANGUAGE, new StringParam("en_CA"));
params.put(IAnyResource.SP_RES_LANGUAGE, new StringParam("en_CA"));
List<IBaseResource> patients = toList(myPatientDao.search(params));
assertEquals(1, patients.size());
assertEquals(id1.toUnqualifiedVersionless(), patients.get(0).getIdElement().toUnqualifiedVersionless());
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
params.put(BaseResource.SP_RES_LANGUAGE, new StringParam("en_US"));
params.put(IAnyResource.SP_RES_LANGUAGE, new StringParam("en_US"));
List<Patient> patients = toList(myPatientDao.search(params));
assertEquals(1, patients.size());
assertEquals(id2.toUnqualifiedVersionless(), patients.get(0).getIdElement().toUnqualifiedVersionless());
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
params.put(BaseResource.SP_RES_LANGUAGE, new StringParam("en_GB"));
params.put(IAnyResource.SP_RES_LANGUAGE, new StringParam("en_GB"));
List<Patient> patients = toList(myPatientDao.search(params));
assertEquals(0, patients.size());
}
@ -628,18 +627,18 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
}
{
SearchParameterMap params = new SearchParameterMap();
params.add(BaseResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US")));
params.add(IAnyResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US")));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1, id2));
}
{
SearchParameterMap params = new SearchParameterMap();
params.add(BaseResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US")));
params.add(IAnyResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US")));
params.setLastUpdated(new DateRangeParam(betweenTime, null));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id2));
}
{
SearchParameterMap params = new SearchParameterMap();
params.add(BaseResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
params.add(IAnyResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
{
@ -647,7 +646,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")));
params.add(BaseResource.SP_RES_LANGUAGE, and);
params.add(IAnyResource.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
{
@ -655,7 +654,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("ZZZZZ")));
params.add(BaseResource.SP_RES_LANGUAGE, and);
params.add(IAnyResource.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty());
}
{
@ -663,7 +662,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("ZZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
params.add(BaseResource.SP_RES_LANGUAGE, and);
params.add(IAnyResource.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty());
}
{
@ -671,7 +670,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("")).addOr(new StringParam(null)));
params.add(BaseResource.SP_RES_LANGUAGE, and);
params.add(IAnyResource.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
{
@ -680,7 +679,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("")).addOr(new StringParam(null)));
params.add(BaseResource.SP_RES_LANGUAGE, and);
params.add(IAnyResource.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
{
@ -688,7 +687,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("")).addOr(new StringParam(null)));
params.add(BaseResource.SP_RES_LANGUAGE, and);
params.add(IAnyResource.SP_RES_LANGUAGE, and);
params.add("_id", new StringParam(id1.getIdPart()));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
@ -766,8 +765,6 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
@Test
public void testSearchLastUpdatedParamWithComparator() throws InterruptedException {
String methodName = "testSearchLastUpdatedParamWithComparator";
IIdType id0;
{
Patient patient = new Patient();
@ -780,7 +777,6 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
long start = System.currentTimeMillis();
Thread.sleep(sleep);
DateTimeType beforeAny = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI);
IIdType id1a;
{
Patient patient = new Patient();
@ -814,17 +810,17 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a, id1b));
map = new SearchParameterMap();
map.setLastUpdated(new DateRangeParam(new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, startDateTime), new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, endDateTime)));
map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, startDateTime), new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, endDateTime)));
ourLog.info("Searching: {}", map.getLastUpdated());
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a, id1b));
map = new SearchParameterMap();
map.setLastUpdated(new DateRangeParam(new DateParam(QuantityCompararatorEnum.GREATERTHAN, startDateTime), new DateParam(QuantityCompararatorEnum.LESSTHAN, endDateTime)));
map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN, startDateTime), new DateParam(ParamPrefixEnum.LESSTHAN, endDateTime)));
ourLog.info("Searching: {}", map.getLastUpdated());
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a, id1b));
map = new SearchParameterMap();
map.setLastUpdated(new DateRangeParam(new DateParam(QuantityCompararatorEnum.GREATERTHAN, startDateTime.getValue()), new DateParam(QuantityCompararatorEnum.LESSTHAN, myPatientDao.read(id1b).getMeta().getLastUpdatedElement().getValue())));
map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN, startDateTime.getValue()), new DateParam(ParamPrefixEnum.LESSTHAN, myPatientDao.read(id1b).getMeta().getLastUpdatedElement().getValue())));
ourLog.info("Searching: {}", map.getLastUpdated());
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a));
}
@ -1372,7 +1368,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
QuantityParam param;
Set<Long> found;
param = new QuantityParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), null, null);
param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), null, null);
found = myObservationDao.searchForIds("value-quantity", param);
int initialSize = found.size();
@ -1383,19 +1379,19 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
myObservationDao.create(o);
param = new QuantityParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), null, null);
param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), null, null);
found = myObservationDao.searchForIds("value-quantity", param);
assertEquals(1 + initialSize, found.size());
param = new QuantityParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), null, methodName + "units");
param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), null, methodName + "units");
found = myObservationDao.searchForIds("value-quantity", param);
assertEquals(1, found.size());
param = new QuantityParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), "urn:bar:" + methodName, null);
param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), "urn:bar:" + methodName, null);
found = myObservationDao.searchForIds("value-quantity", param);
assertEquals(1, found.size());
param = new QuantityParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), "urn:bar:" + methodName, methodName + "units");
param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), "urn:bar:" + methodName, methodName + "units");
found = myObservationDao.searchForIds("value-quantity", param);
assertEquals(1, found.size());
@ -1405,8 +1401,8 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
public void testSearchWithEmptySort() {
SearchParameterMap criteriaUrl = new SearchParameterMap();
DateRangeParam range = new DateRangeParam();
range.setLowerBound(new DateParam(QuantityCompararatorEnum.GREATERTHAN, 1000000));
range.setUpperBound(new DateParam(QuantityCompararatorEnum.LESSTHAN, 2000000));
range.setLowerBound(new DateParam(ParamPrefixEnum.GREATERTHAN, 1000000));
range.setUpperBound(new DateParam(ParamPrefixEnum.LESSTHAN, 2000000));
criteriaUrl.setLastUpdated(range);
criteriaUrl.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.ASC));
IBundleProvider results = myObservationDao.search(criteriaUrl);
@ -1554,7 +1550,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
{
SearchParameterMap params = new SearchParameterMap();
params.add(BaseResource.SP_RES_ID, new StringParam(orgId.getIdPart()));
params.add(IAnyResource.SP_RES_ID, new StringParam(orgId.getIdPart()));
params.addInclude(Organization.INCLUDE_PARTOF.asNonRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
assertThat(resources, contains(orgId, parentOrgId));
@ -1596,7 +1592,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
{
SearchParameterMap params = new SearchParameterMap();
params.add(Organization.SP_RES_ID, new StringParam(orgId.getIdPart()));
params.add(IAnyResource.SP_RES_ID, new StringParam(orgId.getIdPart()));
params.addInclude(Organization.INCLUDE_PARTOF.asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
ourLog.info(resources.toString());
@ -1891,7 +1887,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
ourLog.info("Initial size: " + value.size());
for (IBaseResource next : value.getResources(0, value.size())) {
ourLog.info("Deleting: {}", next.getIdElement());
myDeviceDao.delete((IIdType) next.getIdElement());
myDeviceDao.delete(next.getIdElement());
}
value = myDeviceDao.search(new SearchParameterMap());
@ -2098,7 +2094,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
ValueSet vs2 = new ValueSet();
vs2.setUrl("http://hl7.org/foo/bar");
IIdType id2 = myValueSetDao.create(vs2).getId().toUnqualifiedVersionless();
myValueSetDao.create(vs2).getId().toUnqualifiedVersionless();
IBundleProvider result;
result = myValueSetDao.search(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/basic-resource-type"));

View File

@ -1,35 +0,0 @@
package ca.uhn.fhir.tinder;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import ca.uhn.fhir.tinder.parser.CompartmentParser;
public class CompartmentGeneratorMojo extends AbstractMojo {
@Parameter(required = true)
private String fhirVersion;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
CompartmentParser p = new CompartmentParser(fhirVersion);
try {
p.parse();
} catch (MojoExecutionException e) {
throw e;
} catch (MojoFailureException e) {
throw e;
} catch (Exception e) {
throw new MojoFailureException("Failure during parse", e);
}
}
public static void main(String[] args) throws MojoExecutionException, MojoFailureException {
CompartmentGeneratorMojo mojo = new CompartmentGeneratorMojo();
mojo.fhirVersion = "dstu2";
mojo.execute();
}
}

View File

@ -228,7 +228,7 @@ public class TinderStructuresMojo extends AbstractMojo {
String dtOutputDir = "target/generated-sources/tinder/ca/uhn/fhir/model/dev/composite";
ResourceGeneratorUsingSpreadsheet rp = new ResourceGeneratorUsingSpreadsheet("dstu2", ".");
rp.setBaseResourceNames(Arrays.asList( "supplyrequest"
rp.setBaseResourceNames(Arrays.asList( "patient", "auditevent" , "observation"
// //, "contract"
// "valueset", "organization", "location"
// , "observation", "conformance"

View File

@ -1,5 +1,8 @@
package ca.uhn.fhir.tinder.model;
import java.util.ArrayList;
import java.util.List;
public class Resource extends BaseRootType {
@Override
@ -20,4 +23,20 @@ public class Resource extends BaseRootType {
return name;
}
public SearchParameter getSearchParameterByName(String theName) {
for (SearchParameter next : getSearchParameters()) {
if (next.getName().equalsIgnoreCase(theName)) {
return next;
}
}
return null;
}
public List<String> getSearchParameterNames() {
ArrayList<String> retVal = new ArrayList<String>();
for (SearchParameter next : getSearchParameters()) {
retVal.add(next.getName());
}
return retVal;
}
}

View File

@ -10,6 +10,7 @@ import org.apache.commons.lang3.StringUtils;
public class SearchParameter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParameter.class);
private List<String> myCompartments = new ArrayList<String>();
private List<String> myCompositeOf;
private List<String> myCompositeTypes;
private String myDescription;
@ -18,7 +19,6 @@ public class SearchParameter {
private String myResourceName;
private List<String> myTargetTypes;
private String myType;
private String myVersion;
public SearchParameter(String theVersion, String theResourceName) {
@ -26,6 +26,14 @@ public class SearchParameter {
this.myResourceName = theResourceName;
}
public void addCompartment(String theCompartment) {
myCompartments.add(theCompartment);
}
public List<String> getCompartments() {
return myCompartments;
}
public List<String> getCompositeOf() {
if (myCompositeOf == null) {
myCompositeOf = new ArrayList<String>();

View File

@ -16,6 +16,7 @@ import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@ -404,7 +405,7 @@ public abstract class BaseStructureSpreadsheetParser extends BaseStructureParser
/**
* Subclasses may override
*/
protected void postProcess(BaseElement theTarget) {
protected void postProcess(BaseElement theTarget) throws MojoFailureException {
// nothing
}

View File

@ -1,20 +1,31 @@
package ca.uhn.fhir.tinder.parser;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.maven.plugin.MojoFailureException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import ca.uhn.fhir.tinder.model.BaseElement;
import ca.uhn.fhir.tinder.model.Resource;
import ca.uhn.fhir.tinder.model.SearchParameter;
import ca.uhn.fhir.tinder.util.XMLUtils;
public class CompartmentParser {
private String myVersion;
private Resource myResourceDef;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompartmentParser.class);
public CompartmentParser(String theVersion) {
public CompartmentParser(String theVersion, Resource theResourceDef) {
myVersion = theVersion;
myResourceDef = theResourceDef;
}
public void parse() throws Exception {
@ -45,6 +56,67 @@ public class CompartmentParser {
throw new Exception("Failed to find worksheet with name 'Data Elements' in spreadsheet: " + resName);
}
Element table = (Element) resourcesSheet.getElementsByTagName("Table").item(0);
NodeList rows = table.getElementsByTagName("Row");
Map<Integer, String> col2compartment = new HashMap<Integer, String>();
Element headerRow = (Element) rows.item(0);
for (int i = 1; i < headerRow.getElementsByTagName("Cell").getLength(); i++) {
Element cellElement = (Element) headerRow.getElementsByTagName("Cell").item(i);
Element dataElement = (Element) cellElement.getElementsByTagName("Data").item(0);
col2compartment.put(i, dataElement.getTextContent());
}
Element row = null;
for (int i = 1; i < rows.getLength(); i++) {
Element nextRow = (Element) rows.item(i);
NodeList cells = nextRow.getElementsByTagName("Cell");
Element cellElement = (Element) cells.item(0);
Element dataElement = (Element) cellElement.getElementsByTagName("Data").item(0);
if (dataElement.getTextContent().equals(myResourceDef.getName())) {
row = nextRow;
break;
}
}
if (row == null) {
ourLog.debug("No compartments for resource {}", myResourceDef.getName());
return;
}
NodeList cells = row.getElementsByTagName("Cell");
for (int i = 1; i < cells.getLength(); i++) {
Element cellElement = (Element) cells.item(i);
int index = i;
if (cellElement.hasAttribute("Index")) {
index = Integer.parseInt(cellElement.getAttribute("Index"));
}
String compartment = col2compartment.get(index);
Element dataElement = (Element) cellElement.getElementsByTagName("Data").item(0);
String namesUnsplit = dataElement.getTextContent();
String[] namesSplit = namesUnsplit.split("\\|");
for (String nextName : namesSplit) {
nextName = nextName.trim();
if (isBlank(nextName)) {
continue;
}
String[] parts = nextName.split("\\.");
if (parts[0].equals("{def}")) {
continue;
}
Resource element = myResourceDef;
SearchParameter sp = element.getSearchParameterByName(parts[0]);
if (sp == null) {
throw new MojoFailureException("Can't find child named " + parts[0] + " - Valid names: " + element.getSearchParameterNames());
}
sp.addCompartment(compartment);
}
}
}
}

View File

@ -22,7 +22,7 @@ import com.google.common.reflect.ClassPath.ClassInfo;
public class DatatypeGeneratorUsingSpreadsheet extends BaseStructureSpreadsheetParser {
@Override
protected void postProcess(BaseElement theTarget) {
protected void postProcess(BaseElement theTarget) throws MojoFailureException {
super.postProcess(theTarget);
/*

View File

@ -26,12 +26,21 @@ public class ResourceGeneratorUsingSpreadsheet extends BaseStructureSpreadsheetP
}
@Override
protected void postProcess(BaseElement theTarget) {
protected void postProcess(BaseElement theTarget) throws MojoFailureException {
super.postProcess(theTarget);
if ("Bundle".equals(theTarget.getName())) {
addEverythingToSummary(theTarget);
}
if (getVersion().equals("dstu2") && theTarget instanceof Resource) {
try {
new CompartmentParser(getVersion(), (Resource) theTarget).parse();
} catch (Exception e) {
throw new MojoFailureException(e.toString(), e);
}
}
}
private void addEverythingToSummary(BaseElement theTarget) {

View File

@ -56,6 +56,13 @@ public class ${className} extends ca.uhn.fhir.model.${version}.resource.BaseReso
* </p>
*/
@SearchParamDefinition(name="${param.name}", path="${param.path}", description="${param.description}", type="${param.type}" #{if}($param.compositeOf.empty == false) , compositeOf={ #{foreach}($compositeOf in $param.compositeOf) "${compositeOf}"#{if}($foreach.hasNext), #{end}#{end} } #{end} )
#if ($param.compartments.size() > 0)
@Compartments(providesMembershipIn={
#foreach ($compartment in $param.compartments)
@Compartment(name="$compartment") #{if}($foreach.hasNext), #{end}
#end
})
#end
public static final String $param.constantName = "${param.name}";
/**