535 lines
23 KiB
Java
Raw Normal View History

2014-02-17 17:58:32 -05:00
package ca.uhn.fhir.context;
2014-04-03 14:30:49 -04:00
import static org.apache.commons.lang3.StringUtils.isBlank;
2014-02-17 17:58:32 -05:00
import java.lang.reflect.Field;
2014-02-19 13:02:51 -05:00
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
2014-02-17 17:58:32 -05:00
import java.util.ArrayList;
2014-02-20 12:13:05 -05:00
import java.util.Collection;
2014-02-17 17:58:32 -05:00
import java.util.HashMap;
import java.util.HashSet;
2014-02-18 18:10:50 -05:00
import java.util.Iterator;
2014-02-19 11:59:12 -05:00
import java.util.LinkedList;
2014-02-17 17:58:32 -05:00
import java.util.List;
import java.util.Map;
import java.util.Set;
2014-02-19 11:59:12 -05:00
import java.util.TreeMap;
2014-02-23 18:13:59 -05:00
import java.util.TreeSet;
2014-02-17 17:58:32 -05:00
import ca.uhn.fhir.model.api.BaseResourceReference;
2014-02-17 17:58:32 -05:00
import ca.uhn.fhir.model.api.CodeableConceptElement;
import ca.uhn.fhir.model.api.ICodeEnum;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.ICompositeElement;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
2014-02-20 12:13:05 -05:00
import ca.uhn.fhir.model.api.IResourceBlock;
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
2014-02-20 12:13:05 -05:00
import ca.uhn.fhir.model.api.annotation.Block;
2014-02-18 08:26:49 -05:00
import ca.uhn.fhir.model.api.annotation.Child;
2014-02-18 18:10:50 -05:00
import ca.uhn.fhir.model.api.annotation.CodeTableDef;
2014-02-19 11:59:12 -05:00
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
2014-03-10 12:43:49 -04:00
import ca.uhn.fhir.model.api.annotation.Description;
2014-02-23 18:13:59 -05:00
import ca.uhn.fhir.model.api.annotation.Extension;
2014-02-18 18:10:50 -05:00
import ca.uhn.fhir.model.api.annotation.ResourceDef;
2014-04-03 14:30:49 -04:00
import ca.uhn.fhir.model.dstu.composite.AttachmentDt;
2014-03-17 08:57:57 -04:00
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
2014-03-10 12:43:49 -04:00
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
2014-03-10 18:17:30 -04:00
import ca.uhn.fhir.model.primitive.BoundCodeableConceptDt;
2014-02-24 11:46:08 -05:00
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateDt;
2014-03-20 18:29:34 -04:00
import ca.uhn.fhir.model.primitive.DecimalDt;
2014-02-24 11:46:08 -05:00
import ca.uhn.fhir.model.primitive.ICodedDatatype;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.util.ReflectionUtil;
2014-02-17 17:58:32 -05:00
class ModelScanner {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelScanner.class);
2014-02-18 18:10:50 -05:00
private Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions = new HashMap<Class<? extends IElement>, BaseRuntimeElementDefinition<?>>();
private Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = new HashMap<String, RuntimeResourceDefinition>();
2014-02-18 18:10:50 -05:00
private Map<String, RuntimeResourceDefinition> myNameToResourceDefinitions = new HashMap<String, RuntimeResourceDefinition>();
2014-02-17 17:58:32 -05:00
// private Map<String, RuntimeResourceDefinition>
// myNameToDatatypeDefinitions = new HashMap<String,
// RuntimeDatatypeDefinition>();
2014-02-22 15:33:02 -05:00
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
2014-02-18 08:26:49 -05:00
private Set<Class<? extends IElement>> myScanAlso = new HashSet<Class<? extends IElement>>();
private Set<Class<? extends ICodeEnum>> myScanAlsoCodeTable = new HashSet<Class<? extends ICodeEnum>>();
2014-03-04 16:41:18 -05:00
ModelScanner(Class<? extends IResource> theResourceTypes) throws ConfigurationException {
2014-03-05 17:35:06 -05:00
Set<Class<? extends IElement>> singleton = new HashSet<Class<? extends IElement>>();
2014-03-04 16:41:18 -05:00
singleton.add(theResourceTypes);
init(singleton);
}
2014-03-04 08:13:28 -05:00
ModelScanner(Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException {
2014-03-04 16:41:18 -05:00
init(new HashSet<Class<? extends IElement>>(theResourceTypes));
}
2014-02-18 18:10:50 -05:00
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 = ReflectionUtil.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);
}
}
2014-03-04 16:41:18 -05:00
private void init(Set<Class<? extends IElement>> toScan) {
2014-02-22 15:33:02 -05:00
toScan.add(DateDt.class);
toScan.add(CodeDt.class);
2014-03-20 18:29:34 -04:00
toScan.add(DecimalDt.class);
2014-04-03 14:30:49 -04:00
toScan.add(AttachmentDt.class);
2014-03-20 18:29:34 -04:00
2014-02-18 18:10:50 -05:00
do {
for (Class<? extends IElement> nextClass : toScan) {
scan(nextClass);
}
2014-02-19 11:59:12 -05:00
for (Iterator<Class<? extends IElement>> iter = myScanAlso.iterator(); iter.hasNext();) {
2014-02-18 18:10:50 -05:00
if (myClassToElementDefinitions.containsKey(iter.next())) {
iter.remove();
}
}
toScan.clear();
toScan.addAll(myScanAlso);
myScanAlso.clear();
2014-02-19 13:02:51 -05:00
} while (!toScan.isEmpty());
2014-02-19 11:59:12 -05:00
2014-02-18 18:10:50 -05:00
for (BaseRuntimeElementDefinition<?> next : myClassToElementDefinitions.values()) {
next.sealAndInitialize(myClassToElementDefinitions);
2014-02-17 17:58:32 -05:00
}
2014-02-19 11:59:12 -05:00
2014-02-23 18:13:59 -05:00
myRuntimeChildUndeclaredExtensionDefinition = new RuntimeChildUndeclaredExtensionDefinition();
myRuntimeChildUndeclaredExtensionDefinition.sealAndInitialize(myClassToElementDefinitions);
2014-02-19 11:59:12 -05:00
ourLog.info("Done scanning FHIR library, found {} model entries", myClassToElementDefinitions.size());
2014-02-17 17:58:32 -05:00
}
2014-02-20 12:13:05 -05:00
private void scan(Class<? extends IElement> theClass) throws ConfigurationException {
2014-02-18 18:10:50 -05:00
BaseRuntimeElementDefinition<?> existingDef = myClassToElementDefinitions.get(theClass);
2014-02-17 17:58:32 -05:00
if (existingDef != null) {
2014-02-20 12:13:05 -05:00
return;
2014-02-17 17:58:32 -05:00
}
2014-02-18 18:10:50 -05:00
ResourceDef resourceDefinition = theClass.getAnnotation(ResourceDef.class);
2014-02-17 17:58:32 -05:00
if (resourceDefinition != null) {
if (!IResource.class.isAssignableFrom(theClass)) {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException("Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": " + theClass.getCanonicalName());
2014-02-17 17:58:32 -05:00
}
@SuppressWarnings("unchecked")
Class<? extends IResource> resClass = (Class<? extends IResource>) theClass;
2014-02-20 12:13:05 -05:00
scanResource(resClass, resourceDefinition);
2014-02-17 17:58:32 -05:00
}
2014-02-18 18:10:50 -05:00
DatatypeDef datatypeDefinition = theClass.getAnnotation(DatatypeDef.class);
2014-02-17 17:58:32 -05:00
if (datatypeDefinition != null) {
if (ICompositeDatatype.class.isAssignableFrom(theClass)) {
@SuppressWarnings("unchecked")
Class<? extends ICompositeDatatype> resClass = (Class<? extends ICompositeDatatype>) theClass;
2014-02-20 12:13:05 -05:00
scanCompositeDatatype(resClass, datatypeDefinition);
2014-02-17 17:58:32 -05:00
} else if (IPrimitiveDatatype.class.isAssignableFrom(theClass)) {
2014-02-20 12:13:05 -05:00
@SuppressWarnings({ "unchecked" })
Class<? extends IPrimitiveDatatype<?>> resClass = (Class<? extends IPrimitiveDatatype<?>>) theClass;
scanPrimitiveDatatype(resClass, datatypeDefinition);
2014-02-17 17:58:32 -05:00
} else {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException("Resource type contains a @" + DatatypeDef.class.getSimpleName() + " annotation but does not implement " + IDatatype.class.getCanonicalName() + ": " + theClass.getCanonicalName());
2014-02-17 17:58:32 -05:00
}
}
2014-02-18 18:10:50 -05:00
CodeTableDef codeTableDefinition = theClass.getAnnotation(CodeTableDef.class);
if (codeTableDefinition != null) {
if (ICodeEnum.class.isAssignableFrom(theClass)) {
@SuppressWarnings("unchecked")
Class<? extends ICodeEnum> resClass = (Class<? extends ICodeEnum>) theClass;
2014-02-20 12:13:05 -05:00
scanCodeTable(resClass, codeTableDefinition);
2014-02-18 18:10:50 -05:00
} else {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException("Type contains a @" + CodeTableDef.class.getSimpleName() + " annotation but does not implement " + ICodeEnum.class.getCanonicalName() + ": " + theClass.getCanonicalName());
2014-02-18 18:10:50 -05:00
}
}
2014-02-21 21:06:11 -05:00
2014-02-20 12:13:05 -05:00
Block blockDefinition = theClass.getAnnotation(Block.class);
if (blockDefinition != null) {
if (IResourceBlock.class.isAssignableFrom(theClass)) {
@SuppressWarnings("unchecked")
Class<? extends IResourceBlock> blockClass = (Class<? extends IResourceBlock>) theClass;
scanBlock(blockClass, blockDefinition);
2014-02-21 21:06:11 -05:00
} else {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException("Type contains a @" + Block.class.getSimpleName() + " annotation but does not implement " + IResourceBlock.class.getCanonicalName() + ": " + theClass.getCanonicalName());
2014-02-20 12:13:05 -05:00
}
}
if (blockDefinition == null && codeTableDefinition == null && datatypeDefinition == null && resourceDefinition == null) {
2014-02-21 21:06:11 -05:00
throw new ConfigurationException("Resource type does not contain any valid HAPI-FHIR annotations: " + theClass.getCanonicalName());
2014-02-20 12:13:05 -05:00
}
}
private void scanBlock(Class<? extends IResourceBlock> theClass, Block theBlockDefinition) {
ourLog.debug("Scanning resource block class: {}", theClass.getName());
2014-02-18 18:10:50 -05:00
2014-04-14 16:26:00 -04:00
String resourceName = theBlockDefinition.name(); // TODO: remove name
resourceName = theClass.getCanonicalName();
2014-02-20 12:13:05 -05:00
if (isBlank(resourceName)) {
throw new ConfigurationException("Block type @" + Block.class.getSimpleName() + " annotation contains no name: " + theClass.getCanonicalName());
}
RuntimeResourceBlockDefinition resourceDef = new RuntimeResourceBlockDefinition(resourceName, theClass);
myClassToElementDefinitions.put(theClass, resourceDef);
scanCompositeElementForChildren(theClass, resourceDef);
2014-02-17 17:58:32 -05:00
}
2014-02-22 15:33:02 -05:00
private String scanCodeTable(Class<? extends ICodeEnum> theCodeType, CodeTableDef theCodeTableDefinition) {
return null; // TODO: implement
}
2014-02-20 12:13:05 -05:00
private void scanCompositeDatatype(Class<? extends ICompositeDatatype> theClass, DatatypeDef theDatatypeDefinition) {
2014-02-17 17:58:32 -05:00
ourLog.debug("Scanning resource class: {}", theClass.getName());
RuntimeCompositeDatatypeDefinition resourceDef = new RuntimeCompositeDatatypeDefinition(theDatatypeDefinition, theClass);
2014-02-18 18:10:50 -05:00
myClassToElementDefinitions.put(theClass, resourceDef);
scanCompositeElementForChildren(theClass, resourceDef);
2014-02-17 17:58:32 -05:00
}
2014-02-19 11:59:12 -05:00
@SuppressWarnings("unchecked")
private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, BaseRuntimeElementCompositeDefinition<?> theDefinition) {
2014-02-17 17:58:32 -05:00
Set<String> elementNames = new HashSet<String>();
2014-02-23 18:13:59 -05:00
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToElementDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToExtensionDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
2014-02-19 11:59:12 -05:00
LinkedList<Class<? extends ICompositeElement>> classes = new LinkedList<Class<? extends ICompositeElement>>();
Class<? extends ICompositeElement> current = theClass;
do {
classes.push(current);
if (ICompositeElement.class.isAssignableFrom(current.getSuperclass())) {
current = (Class<? extends ICompositeElement>) current.getSuperclass();
2014-02-19 17:33:46 -05:00
} else {
2014-02-19 11:59:12 -05:00
current = null;
}
2014-02-19 17:33:46 -05:00
} while (current != null);
2014-02-19 11:59:12 -05:00
for (Class<? extends ICompositeElement> next : classes) {
2014-03-25 13:42:38 -04:00
scanCompositeElementForChildren(next, elementNames, orderToElementDef, orderToExtensionDef);
2014-02-19 11:59:12 -05:00
}
// while (orderToElementDef.size() > 0 && orderToElementDef.firstKey() <
// 0) {
// BaseRuntimeDeclaredChildDefinition elementDef =
// orderToElementDef.remove(orderToElementDef.firstKey());
// if (elementDef.getElementName().equals("identifier")) {
// orderToElementDef.put(theIdentifierOrder, elementDef);
// } else {
// throw new ConfigurationException("Don't know how to handle element: "
// + elementDef.getElementName());
// }
// }
2014-02-19 17:33:46 -05:00
2014-02-23 18:13:59 -05:00
TreeSet<Integer> orders = new TreeSet<Integer>();
orders.addAll(orderToElementDef.keySet());
orders.addAll(orderToExtensionDef.keySet());
2014-02-23 18:13:59 -05:00
for (Integer i : orders) {
BaseRuntimeChildDefinition nextChild = orderToElementDef.get(i);
if (nextChild != null) {
theDefinition.addChild(nextChild);
}
BaseRuntimeDeclaredChildDefinition nextExt = orderToExtensionDef.get(i);
if (nextExt != null) {
theDefinition.addExtension((RuntimeChildDeclaredExtensionDefinition) nextExt);
2014-02-19 11:59:12 -05:00
}
}
}
2014-02-19 13:02:51 -05:00
@SuppressWarnings("unchecked")
2014-03-25 13:42:38 -04:00
private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
2014-03-17 08:57:57 -04:00
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
2014-02-28 13:27:35 -05:00
int baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
2014-02-19 11:59:12 -05:00
for (Field next : theClass.getDeclaredFields()) {
2014-02-19 17:33:46 -05:00
2014-03-10 12:43:49 -04:00
Child childAnnotation = next.getAnnotation(Child.class);
if (childAnnotation == null) {
2014-02-19 17:33:46 -05:00
ourLog.debug("Ignoring non-type field '" + next.getName() + "' on target type: " + theClass);
2014-02-17 17:58:32 -05:00
continue;
}
2014-03-10 12:43:49 -04:00
Description descriptionAnnotation = next.getAnnotation(Description.class);
2014-02-17 17:58:32 -05:00
2014-03-10 12:43:49 -04:00
String elementName = childAnnotation.name();
int order = childAnnotation.order();
if (order < 0 && order != Child.ORDER_UNKNOWN) {
throw new ConfigurationException("Invalid order '" + order +"' on @Child for field '" + next.getName()+ "' on target type: " + theClass);
}
if (order != Child.ORDER_UNKNOWN) {
order = order + baseElementOrder;
}
2014-03-10 12:43:49 -04:00
int min = childAnnotation.min();
int max = childAnnotation.max();
2014-02-23 18:13:59 -05:00
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
2014-02-23 18:13:59 -05:00
Extension extensionAttr = next.getAnnotation(Extension.class);
if (extensionAttr != null) {
orderMap = theOrderToExtensionDef;
}
2014-02-20 12:13:05 -05:00
/*
2014-03-17 08:57:57 -04:00
* Anything that's marked as unknown is given a new ID that is <0 so
* that it doesn't conflict wityh any given IDs and can be figured
* out later
2014-02-20 12:13:05 -05:00
*/
2014-02-23 18:13:59 -05:00
while (order == Child.ORDER_UNKNOWN && orderMap.containsKey(order)) {
2014-02-19 11:59:12 -05:00
order--;
}
2014-02-19 17:33:46 -05:00
2014-02-17 17:58:32 -05:00
List<Class<? extends IElement>> choiceTypes = new ArrayList<Class<? extends IElement>>();
2014-03-10 12:43:49 -04:00
for (Class<? extends IElement> nextChoiceType : childAnnotation.type()) {
2014-02-17 17:58:32 -05:00
choiceTypes.add(nextChoiceType);
}
2014-02-23 18:13:59 -05:00
if (orderMap.containsKey(order)) {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException("Detected duplicate field order '" + childAnnotation.order() + "' for element named '" + elementName + "' in type '" + theClass.getCanonicalName() + "'");
2014-02-17 17:58:32 -05:00
}
if (elementNames.contains(elementName)) {
throw new ConfigurationException("Detected duplicate field name '" + elementName + "' in type '" + theClass.getCanonicalName() + "'");
}
2014-02-23 18:13:59 -05:00
Class<?> nextElementType = determineElementType(next);
2014-02-21 21:06:11 -05:00
2014-03-17 08:57:57 -04:00
if (nextElementType.equals(ContainedDt.class)) {
/*
* Child is contained resources
*/
RuntimeChildContainedResources def = new RuntimeChildContainedResources(next, childAnnotation, descriptionAnnotation, elementName);
orderMap.put(order, def);
} else if (choiceTypes.size() > 1 && !ResourceReferenceDt.class.isAssignableFrom(nextElementType)) {
2014-02-18 08:26:49 -05:00
/*
* Child is a choice element
*/
2014-02-21 21:06:11 -05:00
for (Class<? extends IElement> nextType : choiceTypes) {
addScanAlso(nextType);
}
2014-03-10 12:43:49 -04:00
RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
2014-02-23 18:13:59 -05:00
orderMap.put(order, def);
2014-02-17 17:58:32 -05:00
2014-02-23 18:13:59 -05:00
} else if (extensionAttr != null) {
/*
* Child is an extension
*/
Class<? extends IElement> et = (Class<? extends IElement>) nextElementType;
2014-03-17 08:57:57 -04:00
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et);
2014-02-23 18:13:59 -05:00
orderMap.put(order, def);
if (IElement.class.isAssignableFrom(nextElementType)) {
2014-02-23 18:13:59 -05:00
addScanAlso((Class<? extends IElement>) nextElementType);
}
2014-03-10 18:17:30 -04:00
} else if (BaseResourceReference.class.isAssignableFrom(nextElementType)) {
2014-02-18 08:26:49 -05:00
/*
* Child is a resource reference
*/
2014-03-10 12:43:49 -04:00
List<Class<? extends IResource>> refTypesList = new ArrayList<Class<? extends IResource>>();
for (Class<? extends IElement> nextType : childAnnotation.type()) {
if (IResource.class.isAssignableFrom(nextType) == false) {
throw new ConfigurationException("Field '" + next.getName() + "' in class '" + next.getDeclaringClass().getCanonicalName() + "' is of type " + ResourceReferenceDt.class + " but contains a non-resource type: " + nextType.getCanonicalName());
2014-03-10 12:43:49 -04:00
}
refTypesList.add((Class<? extends IResource>) nextType);
2014-02-21 21:06:11 -05:00
addScanAlso(nextType);
}
2014-03-10 12:43:49 -04:00
RuntimeChildResourceDefinition def = new RuntimeChildResourceDefinition(next, elementName, childAnnotation, descriptionAnnotation, refTypesList);
2014-02-23 18:13:59 -05:00
orderMap.put(order, def);
2014-02-18 18:10:50 -05:00
2014-02-20 12:13:05 -05:00
} else if (IResourceBlock.class.isAssignableFrom(nextElementType)) {
/*
2014-03-17 08:57:57 -04:00
* Child is a resource block (i.e. a sub-tag within a resource)
* TODO: do these have a better name according to HL7?
2014-02-20 12:13:05 -05:00
*/
2014-02-21 21:06:11 -05:00
Class<? extends IResourceBlock> blockDef = (Class<? extends IResourceBlock>) nextElementType;
addScanAlso(blockDef);
RuntimeChildResourceBlockDefinition def = new RuntimeChildResourceBlockDefinition(next, childAnnotation, descriptionAnnotation, elementName, blockDef);
2014-02-23 18:13:59 -05:00
orderMap.put(order, def);
2014-02-21 21:06:11 -05:00
2014-03-17 08:57:57 -04:00
} else if (IDatatype.class.equals(nextElementType)) {
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
orderMap.put(order, def);
2014-02-20 12:13:05 -05:00
} else if (IDatatype.class.isAssignableFrom(nextElementType)) {
Class<? extends IDatatype> nextDatatype = (Class<? extends IDatatype>) nextElementType;
2014-02-17 17:58:32 -05:00
2014-02-21 21:06:11 -05:00
addScanAlso(nextDatatype);
2014-02-18 18:10:50 -05:00
BaseRuntimeChildDatatypeDefinition def;
2014-02-20 12:13:05 -05:00
if (IPrimitiveDatatype.class.isAssignableFrom(nextElementType)) {
if (nextElementType.equals(BoundCodeDt.class)) {
IValueSetEnumBinder<Enum<?>> binder = getBoundCodeBinder(next);
2014-03-10 12:43:49 -04:00
def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder);
} else {
2014-03-10 12:43:49 -04:00
def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
}
2014-02-18 18:10:50 -05:00
} else {
2014-03-10 18:17:30 -04:00
if (nextElementType.equals(BoundCodeableConceptDt.class)) {
IValueSetEnumBinder<Enum<?>> binder = getBoundCodeBinder(next);
def = new RuntimeChildCompositeBoundDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder);
} else if (NarrativeDt.class.getSimpleName().equals(nextElementType.getSimpleName())) {
def = new RuntimeChildNarrativeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
2014-03-10 18:17:30 -04:00
} else {
def = new RuntimeChildCompositeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
}
2014-02-18 18:10:50 -05:00
}
CodeableConceptElement concept = next.getAnnotation(CodeableConceptElement.class);
if (concept != null) {
if (!ICodedDatatype.class.isAssignableFrom(nextDatatype)) {
2014-03-17 08:57:57 -04:00
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());
2014-02-18 18:10:50 -05:00
} else {
Class<? extends ICodeEnum> type = concept.type();
2014-02-19 11:59:12 -05:00
myScanAlsoCodeTable.add(type);
2014-02-18 18:10:50 -05:00
def.setCodeType(type);
}
2014-02-17 17:58:32 -05:00
}
2014-02-18 08:26:49 -05:00
2014-02-23 18:13:59 -05:00
orderMap.put(order, def);
2014-02-18 18:10:50 -05:00
2014-02-20 12:13:05 -05:00
} else {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a valid child type: " + nextElementType);
2014-02-17 17:58:32 -05:00
}
elementNames.add(elementName);
}
}
2014-02-22 15:33:02 -05:00
private String scanPrimitiveDatatype(Class<? extends IPrimitiveDatatype<?>> theClass, DatatypeDef theDatatypeDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());
String resourceName = theDatatypeDefinition.name();
if (isBlank(resourceName)) {
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name: " + theClass.getCanonicalName());
2014-02-21 21:06:11 -05:00
}
2014-02-22 15:33:02 -05:00
BaseRuntimeElementDefinition<?> resourceDef;
if (theClass.equals(XhtmlDt.class)) {
@SuppressWarnings("unchecked")
Class<XhtmlDt> clazz = (Class<XhtmlDt>) theClass;
resourceDef = new RuntimePrimitiveDatatypeNarrativeDefinition(resourceName, clazz);
} else {
resourceDef = new RuntimePrimitiveDatatypeDefinition(theDatatypeDefinition, theClass);
2014-02-22 15:33:02 -05:00
}
myClassToElementDefinitions.put(theClass, resourceDef);
return resourceName;
}
private String scanResource(Class<? extends IResource> theClass, ResourceDef resourceDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());
String resourceName = resourceDefinition.name();
if (isBlank(resourceName)) {
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name: " + theClass.getCanonicalName());
}
if (myNameToResourceDefinitions.containsKey(resourceName)) {
if (!myNameToResourceDefinitions.get(resourceName).getImplementingClass().equals(theClass)) {
2014-03-17 08:57:57 -04:00
// throw new
// ConfigurationException("Detected duplicate element name '" +
// resourceName + "' in types '" + theClass.getCanonicalName() +
// "' and '"
// +
// myNameToResourceDefinitions.get(resourceName).getImplementingClass()
// + "'");
2014-03-13 17:33:12 -04:00
} else {
return resourceName;
2014-02-22 15:33:02 -05:00
}
}
String resourceId = resourceDefinition.id();
if (isBlank(resourceId)) {
2014-03-17 08:57:57 -04:00
// throw new ConfigurationException("Resource type @" +
// ResourceDef.class.getSimpleName() +
// " annotation contains no resource ID: " +
// theClass.getCanonicalName());
2014-03-13 17:33:12 -04:00
} else {
if (myIdToResourceDefinition.containsKey(resourceId)) {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException("The following resource types have the same ID of '" + resourceId + "' - " + theClass.getCanonicalName() + " and " + myIdToResourceDefinition.get(resourceId).getImplementingClass().getCanonicalName());
2014-03-13 17:33:12 -04:00
}
}
2014-03-13 17:33:12 -04:00
2014-03-10 12:43:49 -04:00
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(theClass, resourceDefinition);
2014-02-22 15:33:02 -05:00
myClassToElementDefinitions.put(theClass, resourceDef);
myNameToResourceDefinitions.put(resourceName, resourceDef);
scanCompositeElementForChildren(theClass, resourceDef);
2014-02-22 15:33:02 -05:00
myIdToResourceDefinition.put(resourceId, resourceDef);
2014-02-22 15:33:02 -05:00
return resourceName;
2014-02-21 21:06:11 -05:00
}
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;
}
2014-02-17 17:58:32 -05:00
}