More documentation and profile export working
This commit is contained in:
parent
53882b2c23
commit
fefd0e9a57
|
@ -18,6 +18,7 @@ public class FhirContext {
|
|||
private Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition;
|
||||
private final Map<String, RuntimeResourceDefinition> myNameToElementDefinition;
|
||||
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
|
||||
private Map<String, RuntimeResourceDefinition> myIdToResourceDefinition;
|
||||
|
||||
public FhirContext(Class<?>... theResourceTypes) {
|
||||
this(toCollection(theResourceTypes));
|
||||
|
@ -32,6 +33,11 @@ public class FhirContext {
|
|||
myNameToElementDefinition = Collections.unmodifiableMap(scanner.getNameToResourceDefinitions());
|
||||
myClassToElementDefinition = Collections.unmodifiableMap(scanner.getClassToElementDefinitions());
|
||||
myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition();
|
||||
myIdToResourceDefinition = scanner.getIdToResourceDefinition();
|
||||
}
|
||||
|
||||
public RuntimeResourceDefinition getResourceDefinitionById(String theId) {
|
||||
return myIdToResourceDefinition.get(theId);
|
||||
}
|
||||
|
||||
public Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> getClassToElementDefinition() {
|
||||
|
@ -82,4 +88,8 @@ public class FhirContext {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public Collection<RuntimeResourceDefinition> getResourceDefinitions() {
|
||||
return myIdToResourceDefinition.values();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,17 +47,19 @@ class ModelScanner {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelScanner.class);
|
||||
|
||||
private Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions = new HashMap<Class<? extends IElement>, BaseRuntimeElementDefinition<?>>();
|
||||
private Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = new HashMap<String, RuntimeResourceDefinition>();
|
||||
private Map<String, RuntimeResourceDefinition> myNameToResourceDefinitions = new HashMap<String, RuntimeResourceDefinition>();
|
||||
private Set<Class<? extends IElement>> myScanAlso = new HashSet<Class<? extends IElement>>();
|
||||
|
||||
// private Map<String, RuntimeResourceDefinition>
|
||||
// myNameToDatatypeDefinitions = new HashMap<String,
|
||||
// RuntimeDatatypeDefinition>();
|
||||
|
||||
private Set<Class<? extends ICodeEnum>> myScanAlsoCodeTable = new HashSet<Class<? extends ICodeEnum>>();
|
||||
|
||||
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
|
||||
|
||||
private Set<Class<? extends IElement>> myScanAlso = new HashSet<Class<? extends IElement>>();
|
||||
|
||||
private Set<Class<? extends ICodeEnum>> myScanAlsoCodeTable = new HashSet<Class<? extends ICodeEnum>>();
|
||||
|
||||
ModelScanner(Class<? extends IResource> theResourceTypes) throws ConfigurationException {
|
||||
Set<Class<? extends IElement>> singleton = new HashSet<Class<? extends IElement>>();
|
||||
singleton.add(theResourceTypes);
|
||||
|
@ -68,6 +70,56 @@ class ModelScanner {
|
|||
init(new HashSet<Class<? extends IElement>>(theResourceTypes));
|
||||
}
|
||||
|
||||
public Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
|
||||
return myClassToElementDefinitions;
|
||||
}
|
||||
|
||||
public Map<String, RuntimeResourceDefinition> getNameToResourceDefinitions() {
|
||||
return (myNameToResourceDefinitions);
|
||||
}
|
||||
|
||||
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {
|
||||
return myRuntimeChildUndeclaredExtensionDefinition;
|
||||
}
|
||||
|
||||
private void addScanAlso(Class<? extends IElement> theType) {
|
||||
if (theType.isInterface()) {
|
||||
return;
|
||||
}
|
||||
myScanAlso.add(theType);
|
||||
}
|
||||
|
||||
private Class<?> determineElementType(Field next) {
|
||||
Class<?> nextElementType = next.getType();
|
||||
if (List.class.equals(nextElementType)) {
|
||||
nextElementType = getGenericCollectionTypeOfField(next);
|
||||
} else if (Collection.class.isAssignableFrom(nextElementType)) {
|
||||
throw new ConfigurationException("Field '" + next.getName() + "' in type '" + next.getClass().getCanonicalName() + "' is a Collection - Only java.util.List curently supported");
|
||||
}
|
||||
return nextElementType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private IValueSetEnumBinder<Enum<?>> getBoundCodeBinder(Field theNext) {
|
||||
Class<?> bound = getGenericCollectionTypeOfCodedField(theNext);
|
||||
if (bound == null) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has no parameter for " + BoundCodeDt.class.getSimpleName() + " to determine enum type");
|
||||
}
|
||||
|
||||
try {
|
||||
Field bindingField = bound.getField("VALUESET_BINDER");
|
||||
return (IValueSetEnumBinder<Enum<?>>) bindingField.get(null);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field", e);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void init(Set<Class<? extends IElement>> toScan) {
|
||||
toScan.add(DateDt.class);
|
||||
toScan.add(CodeDt.class);
|
||||
|
@ -96,25 +148,6 @@ class ModelScanner {
|
|||
ourLog.info("Done scanning FHIR library, found {} model entries", myClassToElementDefinitions.size());
|
||||
}
|
||||
|
||||
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {
|
||||
return myRuntimeChildUndeclaredExtensionDefinition;
|
||||
}
|
||||
|
||||
public Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
|
||||
return myClassToElementDefinitions;
|
||||
}
|
||||
|
||||
public Map<String, RuntimeResourceDefinition> getNameToResourceDefinitions() {
|
||||
return (myNameToResourceDefinitions);
|
||||
}
|
||||
|
||||
private void addScanAlso(Class<? extends IElement> theType) {
|
||||
if (theType.isInterface()) {
|
||||
return;
|
||||
}
|
||||
myScanAlso.add(theType);
|
||||
}
|
||||
|
||||
private void scan(Class<? extends IElement> theClass) throws ConfigurationException {
|
||||
BaseRuntimeElementDefinition<?> existingDef = myClassToElementDefinitions.get(theClass);
|
||||
if (existingDef != null) {
|
||||
|
@ -266,7 +299,7 @@ class ModelScanner {
|
|||
ourLog.debug("Ignoring non-type field '" + next.getName() + "' on target type: " + theClass);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Description descriptionAnnotation = next.getAnnotation(Description.class);
|
||||
|
||||
String elementName = childAnnotation.name();
|
||||
|
@ -330,7 +363,7 @@ class ModelScanner {
|
|||
*/
|
||||
List<Class<? extends IResource>> refTypesList = new ArrayList<Class<? extends IResource>>();
|
||||
for (Class<? extends IElement> nextType : childAnnotation.type()) {
|
||||
if (IResource.class.isAssignableFrom(nextType)== false) {
|
||||
if (IResource.class.isAssignableFrom(nextType) == false) {
|
||||
throw new ConfigurationException("Field '" + next.getName() + "' is of type " + ResourceReferenceDt.class + " but contains a non-resource type: " + nextType.getCanonicalName());
|
||||
}
|
||||
refTypesList.add((Class<? extends IResource>) nextType);
|
||||
|
@ -347,7 +380,7 @@ class ModelScanner {
|
|||
|
||||
Class<? extends IResourceBlock> blockDef = (Class<? extends IResourceBlock>) nextElementType;
|
||||
addScanAlso(blockDef);
|
||||
RuntimeChildResourceBlockDefinition def = new RuntimeChildResourceBlockDefinition(next, childAnnotation, descriptionAnnotation, elementName, blockDef);
|
||||
RuntimeChildResourceBlockDefinition def = new RuntimeChildResourceBlockDefinition(next, childAnnotation, descriptionAnnotation, elementName, blockDef);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (IDatatype.class.isAssignableFrom(nextElementType)) {
|
||||
|
@ -393,37 +426,6 @@ class ModelScanner {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private IValueSetEnumBinder<Enum<?>> getBoundCodeBinder(Field theNext) {
|
||||
Class<?> bound = getGenericCollectionTypeOfCodedField(theNext);
|
||||
if (bound == null) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has no parameter for " + BoundCodeDt.class.getSimpleName() + " to determine enum type");
|
||||
}
|
||||
|
||||
try {
|
||||
Field bindingField = bound.getField("VALUESET_BINDER");
|
||||
return (IValueSetEnumBinder<Enum<?>>) bindingField.get(null);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field", e);
|
||||
} catch ( IllegalAccessException e) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field", e);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> determineElementType(Field next) {
|
||||
Class<?> nextElementType = next.getType();
|
||||
if (List.class.equals(nextElementType)) {
|
||||
nextElementType = getGenericCollectionTypeOfField(next);
|
||||
} else if (Collection.class.isAssignableFrom(nextElementType)) {
|
||||
throw new ConfigurationException("Field '" + next.getName() + "' in type '" + next.getClass().getCanonicalName() + "' is a Collection - Only java.util.List curently supported");
|
||||
}
|
||||
return nextElementType;
|
||||
}
|
||||
|
||||
private String scanPrimitiveDatatype(Class<? extends IPrimitiveDatatype<?>> theClass, DatatypeDef theDatatypeDefinition) {
|
||||
ourLog.debug("Scanning resource class: {}", theClass.getName());
|
||||
|
||||
|
@ -460,15 +462,42 @@ class ModelScanner {
|
|||
return resourceName;
|
||||
}
|
||||
|
||||
String resourceId = resourceDefinition.id();
|
||||
if (isBlank(resourceId)) {
|
||||
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource ID: " + theClass.getCanonicalName());
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(theClass, resourceDefinition);
|
||||
myClassToElementDefinitions.put(theClass, resourceDef);
|
||||
myNameToResourceDefinitions.put(resourceName, resourceDef);
|
||||
|
||||
scanCompositeElementForChildren(theClass, resourceDef, resourceDefinition.identifierOrder());
|
||||
|
||||
myIdToResourceDefinition.put(resourceId, resourceDef);
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
public Map<String, RuntimeResourceDefinition> getIdToResourceDefinition() {
|
||||
return myIdToResourceDefinition;
|
||||
}
|
||||
|
||||
private static Class<?> getGenericCollectionTypeOfCodedField(Field next) {
|
||||
Class<?> type;
|
||||
ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
|
||||
Type firstArg = collectionType.getActualTypeArguments()[0];
|
||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
||||
ParameterizedType pt = ((ParameterizedType) firstArg);
|
||||
firstArg = pt.getActualTypeArguments()[0];
|
||||
type = (Class<?>) firstArg;
|
||||
} else {
|
||||
type = (Class<?>) firstArg;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private static Class<?> getGenericCollectionTypeOfField(Field next) {
|
||||
// Type genericSuperclass = next.getType().getGenericSuperclass();
|
||||
Class<?> type;
|
||||
|
@ -476,11 +505,11 @@ class ModelScanner {
|
|||
ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
|
||||
Type firstArg = collectionType.getActualTypeArguments()[0];
|
||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
||||
ParameterizedType pt = ((ParameterizedType)firstArg);
|
||||
ParameterizedType pt = ((ParameterizedType) firstArg);
|
||||
type = (Class<?>) pt.getRawType();
|
||||
// firstArg = pt.getActualTypeArguments()[0];
|
||||
// type = (Class<?>) firstArg;
|
||||
}else {
|
||||
// firstArg = pt.getActualTypeArguments()[0];
|
||||
// type = (Class<?>) firstArg;
|
||||
} else {
|
||||
type = (Class<?>) firstArg;
|
||||
}
|
||||
// }else {
|
||||
|
@ -490,19 +519,5 @@ class ModelScanner {
|
|||
// }
|
||||
return type;
|
||||
}
|
||||
|
||||
private static Class<?> getGenericCollectionTypeOfCodedField(Field next) {
|
||||
Class<?> type;
|
||||
ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
|
||||
Type firstArg = collectionType.getActualTypeArguments()[0];
|
||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
||||
ParameterizedType pt = ((ParameterizedType)firstArg);
|
||||
firstArg = pt.getActualTypeArguments()[0];
|
||||
type = (Class<?>) firstArg;
|
||||
}else {
|
||||
type = (Class<?>) firstArg;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
|
|||
public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefinition<IResource> {
|
||||
|
||||
private String myResourceProfile;
|
||||
private Profile myProfileDef;
|
||||
|
||||
public RuntimeResourceDefinition(Class<? extends IResource> theClass, ResourceDef theResourceAnnotation) {
|
||||
super(theResourceAnnotation.name(), theClass);
|
||||
|
@ -44,6 +45,10 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
}
|
||||
|
||||
public synchronized Profile toProfile() {
|
||||
if (myProfileDef != null) {
|
||||
return myProfileDef;
|
||||
}
|
||||
|
||||
Profile retVal = new Profile();
|
||||
RuntimeResourceDefinition def = this;
|
||||
|
||||
|
@ -69,6 +74,8 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
|
||||
retVal.getStructure().get(0).getElement().get(0).getDefinition().addType().getCode().setValue("Resource");
|
||||
|
||||
myProfileDef = retVal;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -132,7 +139,7 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
narrative.getDefinition().setIsModifier(false);
|
||||
narrative.getDefinition().setMin(0);
|
||||
narrative.getDefinition().setMax("1");
|
||||
|
||||
|
||||
StructureElement contained = theStruct.addElement();
|
||||
contained.setName("contained");
|
||||
contained.setPath(join(path, '.') + ".contained");
|
||||
|
@ -141,7 +148,7 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
contained.getDefinition().setMin(0);
|
||||
contained.getDefinition().setMax("1");
|
||||
}
|
||||
|
||||
|
||||
if (def instanceof BaseRuntimeElementCompositeDefinition) {
|
||||
BaseRuntimeElementCompositeDefinition<?> cdef = ((BaseRuntimeElementCompositeDefinition<?>) def);
|
||||
for (BaseRuntimeChildDefinition nextChild : cdef.getChildren()) {
|
||||
|
|
|
@ -13,7 +13,7 @@ public @interface ResourceDef {
|
|||
|
||||
String name();
|
||||
|
||||
String id() default "";
|
||||
String id();
|
||||
|
||||
String profile() default "";
|
||||
|
||||
|
|
|
@ -10,13 +10,16 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Read {
|
||||
|
||||
/**
|
||||
* Returns the resource type that is returned by the method annotated
|
||||
* with this annotation
|
||||
*/
|
||||
Class<? extends IResource> value();
|
||||
|
||||
/**
|
||||
* The return type for this search method. This generally does not need
|
||||
* to be populated for a server implementation, since servers will return
|
||||
* only one resource per class, but generally does need to be populated
|
||||
* for client implementations.
|
||||
*/
|
||||
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
|
||||
Class<? extends IResource> type() default IResource.class;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface IdParam {
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
|||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.common.BaseMethodBinding;
|
||||
|
||||
|
@ -70,7 +71,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
|
|||
ClientInvocationHandler theInvocationHandler = new ClientInvocationHandler(client, myContext, serverBase);
|
||||
|
||||
for (Method nextMethod : theClientType.getMethods()) {
|
||||
BaseMethodBinding binding = BaseMethodBinding.bindMethod(nextMethod);
|
||||
BaseMethodBinding binding = BaseMethodBinding.bindMethod(null, nextMethod);
|
||||
theInvocationHandler.addBinding(nextMethod, binding);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,10 @@ public abstract class BaseMethodBinding {
|
|||
private String myResourceName;
|
||||
private MethodReturnTypeEnum myMethodReturnType;
|
||||
|
||||
public BaseMethodBinding(MethodReturnTypeEnum theMethodReturnType, Class<? extends IResource> theAnnotatedResourceType) {
|
||||
ResourceDef resourceDefAnnotation = theAnnotatedResourceType.getAnnotation(ResourceDef.class);
|
||||
public BaseMethodBinding(MethodReturnTypeEnum theMethodReturnType, Class<? extends IResource> theReturnResourceType) {
|
||||
ResourceDef resourceDefAnnotation = theReturnResourceType.getAnnotation(ResourceDef.class);
|
||||
if (resourceDefAnnotation == null) {
|
||||
throw new ConfigurationException(theAnnotatedResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName() + " annotation");
|
||||
throw new ConfigurationException(theReturnResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName() + " annotation");
|
||||
}
|
||||
myResourceName = resourceDefAnnotation.name();
|
||||
myMethodReturnType = theMethodReturnType;
|
||||
|
@ -39,8 +39,7 @@ public abstract class BaseMethodBinding {
|
|||
|
||||
public abstract GetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException;
|
||||
|
||||
public abstract List<IResource> invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> theParameterValues) throws InvalidRequestException,
|
||||
InternalErrorException;
|
||||
public abstract List<IResource> invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> theParameterValues) throws InvalidRequestException, InternalErrorException;
|
||||
|
||||
public abstract boolean matches(String theResourceName, IdDt theId, IdDt theVersion, Set<String> theParameterNames);
|
||||
|
||||
|
@ -48,25 +47,18 @@ public abstract class BaseMethodBinding {
|
|||
return myResourceName;
|
||||
}
|
||||
|
||||
public static BaseMethodBinding bindMethod(Method theMethod) {
|
||||
public static BaseMethodBinding bindMethod(Class<? extends IResource> theReturnType, Method theMethod) {
|
||||
Read read = theMethod.getAnnotation(Read.class);
|
||||
Search search = theMethod.getAnnotation(Search.class);
|
||||
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends IResource> annotatedResourceType;
|
||||
if (read != null) {
|
||||
annotatedResourceType = read.value();
|
||||
} else {
|
||||
annotatedResourceType = search.value();
|
||||
}
|
||||
|
||||
Class<?> methodReturnType = theMethod.getReturnType();
|
||||
MethodReturnTypeEnum methodReturnTypeEnum;
|
||||
if (methodReturnType.equals(List.class)) {
|
||||
methodReturnTypeEnum = MethodReturnTypeEnum.LIST_OF_RESOURCES;
|
||||
} else if (methodReturnType.isAssignableFrom(annotatedResourceType)) {
|
||||
} else if (IResource.class.isAssignableFrom(methodReturnType)) {
|
||||
methodReturnTypeEnum = MethodReturnTypeEnum.RESOURCE;
|
||||
} else if (Bundle.class.isAssignableFrom(methodReturnType)) {
|
||||
methodReturnTypeEnum = MethodReturnTypeEnum.LIST_OF_RESOURCES;
|
||||
|
@ -74,10 +66,23 @@ public abstract class BaseMethodBinding {
|
|||
throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||
}
|
||||
|
||||
Class<? extends IResource> returnType = theReturnType;
|
||||
if (returnType == null) {
|
||||
if (read != null) {
|
||||
returnType = read.type();
|
||||
} else if (search != null) {
|
||||
returnType = search.type();
|
||||
}
|
||||
|
||||
if (returnType == null) {
|
||||
throw new ConfigurationException("Could not determine return type for method '" + theMethod.getName() + "'. Try explicitly specifying one in the operation annotation.");
|
||||
}
|
||||
}
|
||||
|
||||
if (read != null) {
|
||||
return new ReadMethodBinding(methodReturnTypeEnum, annotatedResourceType, theMethod);
|
||||
return new ReadMethodBinding(methodReturnTypeEnum, returnType, theMethod);
|
||||
} else if (search != null) {
|
||||
return new SearchMethodBinding(methodReturnTypeEnum, annotatedResourceType, theMethod);
|
||||
return new SearchMethodBinding(methodReturnTypeEnum, returnType, theMethod);
|
||||
} else {
|
||||
throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||
}
|
||||
|
@ -111,15 +116,18 @@ public abstract class BaseMethodBinding {
|
|||
if (obj1 == null) {
|
||||
obj1 = object;
|
||||
} else {
|
||||
throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @"
|
||||
+ obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName() + ". Can not have both.");
|
||||
throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @" + obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName()
|
||||
+ ". Can not have both.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (obj1 == null) {
|
||||
return false;
|
||||
// throw new ConfigurationException("Method '" + theNextMethod.getName() + "' on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has no FHIR method annotations.");
|
||||
// throw new ConfigurationException("Method '" +
|
||||
// theNextMethod.getName() + "' on type '" +
|
||||
// theNextMethod.getDeclaringClass().getSimpleName() +
|
||||
// " has no FHIR method annotations.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ public class SearchMethodBinding extends BaseMethodBinding {
|
|||
private RequestType requestType;
|
||||
private Class<?> myDeclaredResourceType;
|
||||
|
||||
public SearchMethodBinding(MethodReturnTypeEnum theMethodReturnTypeEnum, Class<? extends IResource> theAnnotatedResourceType, Method theMethod) {
|
||||
super(theMethodReturnTypeEnum, theAnnotatedResourceType);
|
||||
public SearchMethodBinding(MethodReturnTypeEnum theMethodReturnTypeEnum, Class<? extends IResource> theReturnResourceType, Method theMethod) {
|
||||
super(theMethodReturnTypeEnum, theReturnResourceType);
|
||||
this.method = theMethod;
|
||||
this.myParameters = Util.getResourceParameters(theMethod);
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ public abstract class RestfulServer extends HttpServlet {
|
|||
if (Modifier.isPublic(m.getModifiers())) {
|
||||
ourLog.info("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
|
||||
|
||||
BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m);
|
||||
BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(theProvider.getResourceType(), m);
|
||||
if (foundMethodBinding != null) {
|
||||
r.addMethod(foundMethodBinding);
|
||||
ourLog.info(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
|
||||
|
|
|
@ -8,11 +8,14 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
public @interface Search {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the resource type that is returned by the method annotated
|
||||
* with this annotation
|
||||
* The return type for this search method. This generally does not need
|
||||
* to be populated for a server implementation, since servers will return
|
||||
* only one resource per class, but generally does need to be populated
|
||||
* for client implementations.
|
||||
*/
|
||||
Class<? extends IResource> value();
|
||||
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
|
||||
Class<? extends IResource> type() default IResource.class;
|
||||
|
||||
}
|
|
@ -1,20 +1,58 @@
|
|||
package ca.uhn.fhir.rest.server.provider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Profile;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.operations.Search;
|
||||
|
||||
public class ProfileProvider implements IResourceProvider {
|
||||
|
||||
private FhirContext myContext;
|
||||
|
||||
public ProfileProvider(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Profile.class;
|
||||
}
|
||||
|
||||
public ProfileProvider(FhirContext theCtx) {
|
||||
Profile p = new Profile();
|
||||
// p.
|
||||
@Read()
|
||||
public Profile getProfileById(@Read.IdParam IdDt theId) {
|
||||
RuntimeResourceDefinition retVal = myContext.getResourceDefinitionById(theId.getValue());
|
||||
if (retVal==null) {
|
||||
return null;
|
||||
}
|
||||
return retVal.toProfile();
|
||||
}
|
||||
|
||||
@Search()
|
||||
public List<Profile> getAllProfiles() {
|
||||
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitions());
|
||||
Collections.sort(defs, new Comparator<RuntimeResourceDefinition>() {
|
||||
@Override
|
||||
public int compare(RuntimeResourceDefinition theO1, RuntimeResourceDefinition theO2) {
|
||||
int cmp = theO1.getName().compareTo(theO2.getName());
|
||||
if (cmp==0) {
|
||||
cmp=theO1.getResourceProfile().compareTo(theO2.getResourceProfile());
|
||||
}
|
||||
return cmp;
|
||||
}});
|
||||
ArrayList<Profile> retVal = new ArrayList<Profile>();
|
||||
for (RuntimeResourceDefinition next : defs) {
|
||||
retVal.add(next.toProfile());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class RestfulObservationResourceProvider implements IResourceProvider {
|
|||
* @return
|
||||
* Returns a resource matching this identifier, or null if none exists.
|
||||
*/
|
||||
@Read(Patient.class)
|
||||
@Read()
|
||||
public Patient getResourceById(@Read.IdParam IdDt theId) {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier();
|
||||
|
@ -68,7 +68,7 @@ public class RestfulObservationResourceProvider implements IResourceProvider {
|
|||
* This method returns a list of Patients. This list may contain multiple
|
||||
* matching resources, or it may also be empty.
|
||||
*/
|
||||
@Search(Patient.class)
|
||||
@Search()
|
||||
public List<Patient> getPatient(@Required(name = Patient.SP_FAMILY) StringDt theFamilyName) {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier();
|
||||
|
|
|
@ -32,7 +32,8 @@ public class RestfulPatientResourceProvider implements IResourceProvider {
|
|||
|
||||
/**
|
||||
* The "@Read" annotation indicates that this method supports the
|
||||
* read operation. It takes one argument, the Resource type being returned.
|
||||
* read operation. Read operations should return a single resource
|
||||
* instance.
|
||||
*
|
||||
* @param theId
|
||||
* The read operation takes one parameter, which must be of type
|
||||
|
@ -40,7 +41,7 @@ public class RestfulPatientResourceProvider implements IResourceProvider {
|
|||
* @return
|
||||
* Returns a resource matching this identifier, or null if none exists.
|
||||
*/
|
||||
@Read(Patient.class)
|
||||
@Read()
|
||||
public Patient getResourceById(@Read.IdParam IdDt theId) {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier();
|
||||
|
@ -68,7 +69,7 @@ public class RestfulPatientResourceProvider implements IResourceProvider {
|
|||
* This method returns a list of Patients. This list may contain multiple
|
||||
* matching resources, or it may also be empty.
|
||||
*/
|
||||
@Search(Patient.class)
|
||||
@Search()
|
||||
public List<Patient> getPatient(@Required(name = Patient.SP_FAMILY) StringDt theFamilyName) {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier();
|
||||
|
@ -76,7 +77,7 @@ public class RestfulPatientResourceProvider implements IResourceProvider {
|
|||
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
|
||||
patient.getIdentifier().get(0).setValue("00001");
|
||||
patient.addName();
|
||||
patient.getName().get(0).addFamily("Test");
|
||||
patient.getName().get(0).addFamily(theFamilyName.getValue());
|
||||
patient.getName().get(0).addGiven("PatientOne");
|
||||
patient.getGender().setText("M");
|
||||
return Collections.singletonList(patient);
|
||||
|
@ -84,3 +85,5 @@ public class RestfulPatientResourceProvider implements IResourceProvider {
|
|||
|
||||
}
|
||||
//END SNIPPET: provider
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.operations.Search;
|
||||
|
||||
public class RestfulPatientResourceProviderMore implements IResourceProvider {
|
||||
|
||||
//START SNIPPET: searchAll
|
||||
@Search
|
||||
public List<Organization> getAllOrganizations() {
|
||||
List<Organization> retVal=new ArrayList<Organization>(); // populate this
|
||||
return retVal;
|
||||
}
|
||||
//END SNIPPET: searchAll
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +94,25 @@
|
|||
|
||||
</section>
|
||||
|
||||
<section name="Resource Providers">
|
||||
|
||||
<p>
|
||||
Resource providers can support a wide variety of operations.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following example shows a search with no parameters. This operation
|
||||
should return all resources of a given type (this obviously doesn't make
|
||||
sense in all contexts, but does for some resource types).
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="searchAll" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</document>
|
||||
|
|
|
@ -12,7 +12,7 @@ import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
|||
import ca.uhn.fhir.model.primitive.DateDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
|
||||
@ResourceDef(name = "ResourceWithExtensionsA")
|
||||
@ResourceDef(name = "ResourceWithExtensionsA", id="0001")
|
||||
public class ResourceWithExtensionsA implements IResource {
|
||||
|
||||
/*
|
||||
|
|
|
@ -11,13 +11,13 @@ import ca.uhn.fhir.rest.server.parameters.Required;
|
|||
|
||||
public interface ITestClient extends IRestfulClient {
|
||||
|
||||
@Read(value=Patient.class)
|
||||
@Read(type=Patient.class)
|
||||
Patient getPatientById(@Read.IdParam IdDt theId);
|
||||
|
||||
@Search(value=Patient.class)
|
||||
@Search(type=Patient.class)
|
||||
Patient findPatientByMrn(@Required(name = Patient.SP_IDENTIFIER) IdentifierDt theId);
|
||||
|
||||
@Search(value=Patient.class)
|
||||
@Search(type=Patient.class)
|
||||
Bundle findPatientByLastName(@Required(name = Patient.SP_FAMILY) IdentifierDt theId);
|
||||
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public class DummyPatientResourceProvider implements IResourceProvider {
|
|||
}
|
||||
}
|
||||
|
||||
@Search(Patient.class)
|
||||
@Search()
|
||||
public Patient getPatient(@Required(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
|
||||
for (Patient next : myIdToPatient.values()) {
|
||||
for (IdentifierDt nextId : next.getIdentifier()) {
|
||||
|
@ -73,7 +73,7 @@ public class DummyPatientResourceProvider implements IResourceProvider {
|
|||
* The resource identity
|
||||
* @return The resource
|
||||
*/
|
||||
@Read(Patient.class)
|
||||
@Read()
|
||||
public Patient getResourceById(@Read.IdParam IdDt theId) {
|
||||
return myIdToPatient.get(theId.getValue());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue