905 lines
40 KiB
Java
Raw Normal View History

2014-02-17 17:58:32 -05:00
package ca.uhn.fhir.context;
/*
* #%L
* HAPI FHIR - Core Library
* %%
2016-01-07 08:50:07 -05:00
* Copyright (C) 2014 - 2016 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
2014-02-17 17:58:32 -05:00
import java.io.IOException;
import java.io.InputStream;
2014-12-10 17:40:47 -05:00
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
2014-02-17 17:58:32 -05:00
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
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;
import java.util.LinkedHashMap;
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;
2014-07-02 08:57:07 -04:00
import java.util.Map.Entry;
import java.util.Properties;
2014-02-17 17:58:32 -05:00
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
2015-05-22 17:32:36 -04:00
import org.hl7.fhir.instance.model.api.IAnyResource;
2015-06-21 22:10:13 -04:00
import org.hl7.fhir.instance.model.api.IBase;
2015-05-05 13:16:10 -04:00
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
2015-02-16 11:33:46 -05:00
import org.hl7.fhir.instance.model.api.IBaseDatatype;
2015-06-21 22:10:13 -04:00
import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
2015-04-13 08:32:49 -04:00
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
2015-02-16 11:33:46 -05:00
import org.hl7.fhir.instance.model.api.IBaseExtension;
2015-06-21 22:10:13 -04:00
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
2015-04-13 08:32:49 -04:00
import org.hl7.fhir.instance.model.api.IBaseXhtml;
2015-06-21 22:10:13 -04:00
import org.hl7.fhir.instance.model.api.ICompositeType;
2015-04-13 08:32:49 -04:00
import org.hl7.fhir.instance.model.api.IIdType;
2015-02-16 11:33:46 -05:00
import org.hl7.fhir.instance.model.api.INarrative;
2015-06-21 22:10:13 -04:00
import org.hl7.fhir.instance.model.api.IPrimitiveType;
2014-12-10 14:28:25 -05:00
2014-05-28 16:07:53 -04:00
import ca.uhn.fhir.model.api.ExtensionDt;
2014-10-16 13:41:57 -04:00
import ca.uhn.fhir.model.api.IBoundCodeableConcept;
2014-02-17 17:58:32 -05:00
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
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;
2016-05-28 10:35:08 -04:00
import ca.uhn.fhir.model.api.annotation.ChildOrder;
2016-02-26 18:16:35 -05:00
import ca.uhn.fhir.model.api.annotation.Compartment;
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;
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
import ca.uhn.fhir.model.base.composite.BaseNarrativeDt;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
2014-02-24 11:46:08 -05:00
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
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-12-10 14:28:25 -05:00
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>();
private Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinitions = new HashMap<String, BaseRuntimeElementDefinition<?>>();
2014-02-18 18:10:50 -05:00
private Map<String, RuntimeResourceDefinition> myNameToResourceDefinitions = new HashMap<String, RuntimeResourceDefinition>();
private Map<String, Class<? extends IBaseResource>> myNameToResourceType = new HashMap<String, Class<? extends IBaseResource>>();
2014-02-22 15:33:02 -05:00
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
2014-12-10 14:28:25 -05:00
private Set<Class<? extends IBase>> myScanAlso = new HashSet<Class<? extends IBase>>();
private FhirVersionEnum myVersion;
2015-05-02 14:04:43 -07:00
private Set<Class<? extends IBase>> myVersionTypes;
2016-02-26 18:16:35 -05:00
ModelScanner(FhirContext theContext, FhirVersionEnum theVersion, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingDefinitions,
Collection<Class<? extends IBase>> theResourceTypes) throws ConfigurationException {
2014-12-01 08:13:32 -05:00
myContext = theContext;
myVersion = theVersion;
Set<Class<? extends IBase>> toScan;
2014-12-01 08:13:32 -05:00
if (theResourceTypes != null) {
toScan = new HashSet<Class<? extends IBase>>(theResourceTypes);
2014-12-01 08:13:32 -05:00
} else {
toScan = new HashSet<Class<? extends IBase>>();
2014-12-01 08:13:32 -05:00
}
init(theExistingDefinitions, toScan);
2014-03-04 16:41:18 -05:00
}
2014-02-18 18:10:50 -05:00
2014-12-10 17:40:47 -05:00
private void addScanAlso(Class<? extends IBase> theType) {
2015-02-16 11:33:46 -05:00
if (theType.isInterface() || Modifier.isAbstract(theType.getModifiers())) {
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);
}
}
public Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
return myClassToElementDefinitions;
}
public Map<String, RuntimeResourceDefinition> getIdToResourceDefinition() {
return myIdToResourceDefinition;
}
public Map<String, BaseRuntimeElementDefinition<?>> getNameToElementDefinitions() {
return myNameToElementDefinitions;
}
public Map<String, RuntimeResourceDefinition> getNameToResourceDefinition() {
return myNameToResourceDefinitions;
}
public Map<String, RuntimeResourceDefinition> getNameToResourceDefinitions() {
return (myNameToResourceDefinitions);
}
public Map<String, Class<? extends IBaseResource>> getNameToResourceType() {
return myNameToResourceType;
}
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {
return myRuntimeChildUndeclaredExtensionDefinition;
}
private void init(Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Set<Class<? extends IBase>> theDatatypes) {
2014-07-04 09:27:36 -04:00
if (theExistingDefinitions != null) {
2014-07-02 08:57:07 -04:00
myClassToElementDefinitions.putAll(theExistingDefinitions);
}
2014-07-04 09:27:36 -04:00
2014-07-02 08:57:07 -04:00
int startSize = myClassToElementDefinitions.size();
long start = System.currentTimeMillis();
Map<String, Class<? extends IBaseResource>> resourceTypes = myNameToResourceType;
2015-05-02 14:04:43 -07:00
myVersionTypes = scanVersionPropertyFile(theDatatypes, resourceTypes, myVersion);
2016-02-26 18:16:35 -05:00
// toScan.add(DateDt.class);
// toScan.add(CodeDt.class);
// toScan.add(DecimalDt.class);
// toScan.add(AttachmentDt.class);
// toScan.add(ResourceReferenceDt.class);
2014-05-28 16:07:53 -04:00
// toScan.add(QuantityDt.class);
2014-02-18 18:10:50 -05:00
do {
for (Class<? extends IBase> nextClass : theDatatypes) {
2014-02-18 18:10:50 -05:00
scan(nextClass);
}
2014-12-10 14:28:25 -05:00
for (Iterator<Class<? extends IBase>> iter = myScanAlso.iterator(); iter.hasNext();) {
2014-02-18 18:10:50 -05:00
if (myClassToElementDefinitions.containsKey(iter.next())) {
iter.remove();
}
}
theDatatypes.clear();
theDatatypes.addAll(myScanAlso);
2014-02-18 18:10:50 -05:00
myScanAlso.clear();
} while (!theDatatypes.isEmpty());
2014-02-19 11:59:12 -05:00
2014-12-10 14:28:25 -05:00
for (Entry<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> nextEntry : myClassToElementDefinitions.entrySet()) {
2014-07-04 09:27:36 -04:00
if (theExistingDefinitions != null && theExistingDefinitions.containsKey(nextEntry.getKey())) {
2014-07-02 08:57:07 -04:00
continue;
}
BaseRuntimeElementDefinition<?> next = nextEntry.getValue();
next.sealAndInitialize(myContext, 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(myContext, myClassToElementDefinitions);
long time = System.currentTimeMillis() - start;
2014-07-04 09:27:36 -04:00
int size = myClassToElementDefinitions.size() - startSize;
ourLog.debug("Done scanning FHIR library, found {} model entries in {}ms", size, time);
2014-02-17 17:58:32 -05:00
}
private boolean isStandardType(Class<? extends IBase> theClass) {
2015-12-30 11:27:12 -06:00
boolean retVal = myVersionTypes.contains(theClass);
return retVal;
}
/**
2016-02-26 18:16:35 -05:00
* 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.
*/
2016-02-26 18:16:35 -05:00
private <T extends Annotation> T pullAnnotation(AnnotatedElement theTarget, Class<T> theAnnotationType) {
T retVal = theTarget.getAnnotation(theAnnotationType);
2016-02-26 18:16:35 -05:00
return retVal;
}
2014-12-10 14:28:25 -05:00
private void scan(Class<? extends IBase> 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
}
2016-02-26 18:16:35 -05:00
ResourceDef resourceDefinition = pullAnnotation(theClass, ResourceDef.class);
2014-02-17 17:58:32 -05:00
if (resourceDefinition != null) {
2015-02-16 11:33:46 -05:00
if (!IBaseResource.class.isAssignableFrom(theClass)) {
2016-02-26 18:16:35 -05: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")
2014-12-10 17:40:47 -05:00
Class<? extends IBaseResource> resClass = (Class<? extends IBaseResource>) theClass;
2014-02-20 12:13:05 -05:00
scanResource(resClass, resourceDefinition);
2014-02-17 17:58:32 -05:00
}
2016-02-26 18:16:35 -05:00
DatatypeDef datatypeDefinition = pullAnnotation(theClass, DatatypeDef.class);
2014-02-17 17:58:32 -05:00
if (datatypeDefinition != null) {
2014-12-10 14:28:25 -05:00
if (ICompositeType.class.isAssignableFrom(theClass)) {
2014-02-17 17:58:32 -05:00
@SuppressWarnings("unchecked")
2014-12-10 14:28:25 -05:00
Class<? extends ICompositeType> resClass = (Class<? extends ICompositeType>) theClass;
2014-02-20 12:13:05 -05:00
scanCompositeDatatype(resClass, datatypeDefinition);
2014-12-10 14:28:25 -05:00
} else if (IPrimitiveType.class.isAssignableFrom(theClass)) {
2014-02-20 12:13:05 -05:00
@SuppressWarnings({ "unchecked" })
2014-12-14 22:29:15 -05:00
Class<? extends IPrimitiveType<?>> resClass = (Class<? extends IPrimitiveType<?>>) theClass;
2014-02-20 12:13:05 -05:00
scanPrimitiveDatatype(resClass, datatypeDefinition);
2014-02-17 17:58:32 -05:00
} else {
return;
2016-02-26 18:16:35 -05: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
}
}
2016-02-26 18:16:35 -05:00
Block blockDefinition = pullAnnotation(theClass, Block.class);
2015-02-16 11:33:46 -05:00
2014-02-20 12:13:05 -05:00
if (blockDefinition != null) {
2015-05-05 13:16:10 -04:00
if (IResourceBlock.class.isAssignableFrom(theClass) || IBaseBackboneElement.class.isAssignableFrom(theClass) || IBaseDatatypeElement.class.isAssignableFrom(theClass)) {
2015-02-16 11:33:46 -05:00
scanBlock(theClass);
2014-02-21 21:06:11 -05:00
} else {
2016-02-26 18:16:35 -05: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
}
}
2014-08-07 08:27:52 -04:00
if (blockDefinition == null && datatypeDefinition == null && resourceDefinition == null) {
throw new ConfigurationException("Resource class[" + theClass.getName() + "] does not contain any valid HAPI-FHIR annotations");
2014-02-20 12:13:05 -05:00
}
}
2015-02-16 11:33:46 -05:00
private void scanBlock(Class<? extends IBase> theClass) {
2014-02-20 12:13:05 -05:00
ourLog.debug("Scanning resource block class: {}", theClass.getName());
2014-02-18 18:10:50 -05:00
2014-08-07 08:27:52 -04:00
String 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());
}
2015-05-02 14:04:43 -07:00
RuntimeResourceBlockDefinition resourceDef = new RuntimeResourceBlockDefinition(resourceName, theClass, isStandardType(theClass));
2014-02-20 12:13:05 -05:00
myClassToElementDefinitions.put(theClass, resourceDef);
scanCompositeElementForChildren(theClass, resourceDef);
2014-02-17 17:58:32 -05:00
}
2014-12-10 17:40:47 -05:00
private void scanCompositeDatatype(Class<? extends ICompositeType> theClass, DatatypeDef theDatatypeDefinition) {
2015-02-18 17:00:47 -05:00
ourLog.debug("Scanning datatype class: {}", theClass.getName());
2014-02-17 17:58:32 -05:00
2014-05-28 16:07:53 -04:00
RuntimeCompositeDatatypeDefinition resourceDef;
if (theClass.equals(ExtensionDt.class)) {
2015-05-02 14:04:43 -07:00
resourceDef = new RuntimeExtensionDtDefinition(theDatatypeDefinition, theClass, true);
2016-02-26 18:16:35 -05:00
// } else if (IBaseMetaType.class.isAssignableFrom(theClass)) {
// resourceDef = new RuntimeMetaDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
2014-05-28 16:07:53 -04:00
} else {
2015-05-02 14:04:43 -07:00
resourceDef = new RuntimeCompositeDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
2014-05-28 16:07:53 -04:00
}
2014-02-18 18:10:50 -05:00
myClassToElementDefinitions.put(theClass, resourceDef);
myNameToElementDefinitions.put(resourceDef.getName().toLowerCase(), resourceDef);
scanCompositeElementForChildren(theClass, resourceDef);
2014-02-17 17:58:32 -05:00
}
2014-02-19 11:59:12 -05:00
@SuppressWarnings("unchecked")
2014-12-10 17:40:47 -05:00
private void scanCompositeElementForChildren(Class<? extends IBase> 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
2014-12-10 17:40:47 -05:00
LinkedList<Class<? extends IBase>> classes = new LinkedList<Class<? extends IBase>>();
2014-08-15 15:55:39 -04:00
/*
2014-08-15 15:55:39 -04:00
* We scan classes for annotated fields in the class but also all of its superclasses
*/
2014-12-10 17:40:47 -05:00
Class<? extends IBase> current = theClass;
2016-05-28 10:35:08 -04:00
Map<String, Integer> forcedOrder = null;
2014-02-19 11:59:12 -05:00
do {
2016-05-28 10:35:08 -04:00
if (forcedOrder == null) {
ChildOrder childOrder = current.getAnnotation(ChildOrder.class);
if (childOrder != null) {
forcedOrder = new HashMap<String, Integer>();
for (int i = 0; i < childOrder.names().length; i++) {
forcedOrder.put(childOrder.names()[i], i);
}
}
}
2014-02-19 11:59:12 -05:00
classes.push(current);
2015-02-16 11:33:46 -05:00
if (IBase.class.isAssignableFrom(current.getSuperclass())) {
2014-12-10 17:40:47 -05:00
current = (Class<? extends IBase>) 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-12-10 17:40:47 -05:00
for (Class<? extends IBase> next : classes) {
2016-05-28 12:53:59 -04:00
scanCompositeElementForChildren(next, elementNames, orderToElementDef, orderToExtensionDef);
2014-02-19 11:59:12 -05:00
}
2016-05-28 12:53:59 -04:00
if (forcedOrder != null) {
/*
* Find out how many elements don't match any entry in the list
* for forced order. Those elements come first.
*/
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> newOrderToExtensionDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
int unknownCount = 0;
for (BaseRuntimeDeclaredChildDefinition nextEntry : orderToElementDef.values()) {
if (!forcedOrder.containsKey(nextEntry.getElementName())) {
newOrderToExtensionDef.put(unknownCount, nextEntry);
unknownCount++;
}
}
for (BaseRuntimeDeclaredChildDefinition nextEntry : orderToElementDef.values()) {
if (forcedOrder.containsKey(nextEntry.getElementName())) {
Integer newOrder = forcedOrder.get(nextEntry.getElementName());
newOrderToExtensionDef.put(newOrder + unknownCount, nextEntry);
}
}
orderToElementDef = newOrderToExtensionDef;
}
// 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")
2016-02-26 18:16:35 -05:00
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
2016-05-28 12:53:59 -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
if (Modifier.isFinal(next.getModifiers())) {
2014-12-01 08:13:32 -05:00
ourLog.trace("Ignoring constant {} on target type {}", next.getName(), theClass);
continue;
}
2014-12-22 12:03:02 -05:00
2016-02-26 18:16:35 -05:00
Child childAnnotation = pullAnnotation(next, Child.class);
2014-03-10 12:43:49 -04:00
if (childAnnotation == null) {
2014-12-01 08:13:32 -05:00
ourLog.trace("Ignoring non @Child field {} on target type {}", next.getName(), theClass);
2014-02-17 17:58:32 -05:00
continue;
}
2016-02-26 18:16:35 -05:00
Description descriptionAnnotation = pullAnnotation(next, Description.class);
2014-02-17 17:58:32 -05:00
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
2016-02-26 18:16:35 -05:00
Extension extensionAttr = pullAnnotation(next, Extension.class);
if (extensionAttr != null) {
orderMap = theOrderToExtensionDef;
}
2014-08-15 15:55:39 -04:00
2014-03-10 12:43:49 -04:00
String elementName = childAnnotation.name();
int order = childAnnotation.order();
boolean childIsChoiceType = false;
if (order == Child.REPLACE_PARENT) {
2014-08-15 15:55:39 -04:00
if (extensionAttr != null) {
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
if (nextDef instanceof RuntimeChildDeclaredExtensionDefinition) {
if (nextDef.getExtensionUrl().equals(extensionAttr.url())) {
order = nextEntry.getKey();
orderMap.remove(nextEntry.getKey());
elementNames.remove(elementName);
break;
}
}
}
if (order == Child.REPLACE_PARENT) {
2016-02-26 18:16:35 -05:00
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());
2014-08-15 15:55:39 -04:00
}
} else {
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
if (elementName.equals(nextDef.getElementName())) {
order = nextEntry.getKey();
BaseRuntimeDeclaredChildDefinition existing = orderMap.remove(nextEntry.getKey());
2014-08-15 15:55:39 -04:00
elementNames.remove(elementName);
/*
* See #350 - If the original field (in the superclass) with the given name is a choice, then we need to make sure
* that the field which replaces is a choice even if it's only a choice of one type - this is because the
* element name when serialized still needs to reflect the datatype
*/
if (existing instanceof RuntimeChildChoiceDefinition) {
childIsChoiceType = true;
}
2014-08-15 15:55:39 -04:00
break;
}
}
if (order == Child.REPLACE_PARENT) {
2016-02-26 18:16:35 -05:00
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());
2014-08-15 15:55:39 -04:00
}
}
2014-08-15 15:55:39 -04:00
}
2016-05-28 10:35:08 -04:00
2016-05-28 12:53:59 -04:00
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;
}
2015-02-16 11:33:46 -05:00
// int min = childAnnotation.min();
// int max = childAnnotation.max();
2014-02-20 12:13:05 -05:00
/*
2016-02-26 18:16:35 -05:00
* 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
2014-02-20 12:13:05 -05:00
*/
if (order == Child.ORDER_UNKNOWN) {
order = Integer.valueOf(0);
while (orderMap.containsKey(order)) {
order++;
}
2014-02-19 11:59:12 -05:00
}
2014-02-19 17:33:46 -05:00
2014-12-10 17:40:47 -05:00
List<Class<? extends IBase>> choiceTypes = new ArrayList<Class<? extends IBase>>();
for (Class<? extends IBase> 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)) {
2016-05-28 12:53:59 -04:00
throw new ConfigurationException("Detected duplicate field order '" + childAnnotation.order() + "' for element named '" + elementName + "' in type '" + theClass.getCanonicalName() + "' - Already had: " + orderMap.get(order).getElementName());
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
if (childAnnotation.name().equals("extension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
RuntimeChildExtension def = new RuntimeChildExtension(next, childAnnotation.name(), childAnnotation, descriptionAnnotation);
orderMap.put(order, def);
} else if (childAnnotation.name().equals("modifierExtension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
RuntimeChildExtension def = new RuntimeChildExtension(next, childAnnotation.name(), childAnnotation, descriptionAnnotation);
orderMap.put(order, def);
} else if (BaseContainedDt.class.isAssignableFrom(nextElementType) || (childAnnotation.name().equals("contained") && IBaseResource.class.isAssignableFrom(nextElementType))) {
2015-02-16 11:33:46 -05:00
/*
2015-04-13 08:32:49 -04:00
* Child is contained resources
2015-02-16 11:33:46 -05:00
*/
2015-04-13 08:32:49 -04:00
RuntimeChildContainedResources def = new RuntimeChildContainedResources(next, childAnnotation, descriptionAnnotation, elementName);
2015-02-16 11:33:46 -05:00
orderMap.put(order, def);
2015-05-22 17:32:36 -04:00
} else if (IAnyResource.class.isAssignableFrom(nextElementType) || IResource.class.equals(nextElementType)) {
2014-03-17 08:57:57 -04:00
/*
2015-04-13 08:32:49 -04:00
* Child is a resource as a direct child, as in Bundle.entry.resource
2014-03-17 08:57:57 -04:00
*/
2015-04-13 08:32:49 -04:00
RuntimeChildDirectResource def = new RuntimeChildDirectResource(next, childAnnotation, descriptionAnnotation, elementName);
2014-03-17 08:57:57 -04:00
orderMap.put(order, def);
} else {
childIsChoiceType |= choiceTypes.size() > 1;
if (childIsChoiceType && !BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) && !IBaseReference.class.isAssignableFrom(nextElementType)) {
/*
* Child is a choice element
*/
for (Class<? extends IBase> nextType : choiceTypes) {
addScanAlso(nextType);
}
RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
orderMap.put(order, def);
2015-03-11 09:11:36 -04:00
} else if (next.getType().equals(ExtensionDt.class)) {
RuntimeChildExtensionDt def = new RuntimeChildExtensionDt(next, elementName, childAnnotation, descriptionAnnotation);
orderMap.put(order, def);
if (IElement.class.isAssignableFrom(nextElementType)) {
addScanAlso((Class<? extends IElement>) nextElementType);
2014-03-10 12:43:49 -04:00
}
2014-02-18 18:10:50 -05:00
} else if (extensionAttr != null) {
/*
* Child is an extension
*/
Class<? extends IBase> et = (Class<? extends IBase>) nextElementType;
2014-02-21 21:06:11 -05:00
Object binder = null;
if (BoundCodeDt.class.isAssignableFrom(nextElementType) || IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
binder = getBoundCodeBinder(next);
}
2014-02-21 21:06:11 -05:00
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et,
binder);
if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
def.setEnumerationType(ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next));
}
2014-03-17 08:57:57 -04:00
orderMap.put(order, def);
if (IBase.class.isAssignableFrom(nextElementType)) {
addScanAlso((Class<? extends IBase>) nextElementType);
}
} else if (BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) || IBaseReference.class.isAssignableFrom(nextElementType)) {
/*
* Child is a resource reference
*/
List<Class<? extends IBaseResource>> refTypesList = new ArrayList<Class<? extends IBaseResource>>();
for (Class<? extends IElement> nextType : childAnnotation.type()) {
if (IBaseReference.class.isAssignableFrom(nextType)) {
refTypesList.add(myVersion.isRi() ? IAnyResource.class : IResource.class);
continue;
} else 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());
}
refTypesList.add((Class<? extends IBaseResource>) nextType);
addScanAlso(nextType);
}
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)) {
/*
* 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;
addScanAlso(blockDef);
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)) {
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)) {
Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
addScanAlso(nextDatatype);
BaseRuntimeChildDatatypeDefinition def;
if (IPrimitiveType.class.isAssignableFrom(nextElementType)) {
if (nextElementType.equals(BoundCodeDt.class)) {
IValueSetEnumBinder<Enum<?>> binder = getBoundCodeBinder(next);
Class<? extends Enum<?>> enumType = determineEnumTypeForBoundField(next);
def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
} else if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
Class<? extends Enum<?>> binderType = determineEnumTypeForBoundField(next);
def = new RuntimeChildPrimitiveEnumerationDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binderType);
} else {
def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
}
} else if (IBaseXhtml.class.isAssignableFrom(nextElementType)) {
def = new RuntimeChildXhtmlDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
2014-03-10 18:17:30 -04:00
} else {
if (IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
IValueSetEnumBinder<Enum<?>> binder = getBoundCodeBinder(next);
Class<? extends Enum<?>> enumType = determineEnumTypeForBoundField(next);
def = new RuntimeChildCompositeBoundDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
} else if (BaseNarrativeDt.class.isAssignableFrom(nextElementType) || INarrative.class.isAssignableFrom(nextElementType)) {
def = new RuntimeChildNarrativeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
} else {
def = new RuntimeChildCompositeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
}
2014-03-10 18:17:30 -04:00
}
2014-02-18 18:10:50 -05:00
orderMap.put(order, def);
2014-02-18 18:10:50 -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);
}
}
2016-03-18 19:41:43 +01:00
private Class<? extends Enum<?>> determineEnumTypeForBoundField(Field next) {
@SuppressWarnings("unchecked")
Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next);
return enumType;
}
private String scanPrimitiveDatatype(Class<? extends IPrimitiveType<?>> theClass, DatatypeDef theDatatypeDefinition) {
2014-02-22 15:33:02 -05:00
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;
2015-05-02 14:04:43 -07:00
resourceDef = new RuntimePrimitiveDatatypeNarrativeDefinition(resourceName, clazz, isStandardType(clazz));
2015-04-13 08:32:49 -04:00
} else if (IBaseXhtml.class.isAssignableFrom(theClass)) {
@SuppressWarnings("unchecked")
Class<? extends IBaseXhtml> clazz = (Class<? extends IBaseXhtml>) theClass;
2015-05-02 14:04:43 -07:00
resourceDef = new RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition(resourceName, clazz, isStandardType(clazz));
2015-04-13 08:32:49 -04:00
} else if (IIdType.class.isAssignableFrom(theClass)) {
2015-05-02 14:04:43 -07:00
resourceDef = new RuntimeIdDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
2014-02-22 15:33:02 -05:00
} else {
2015-05-02 14:04:43 -07:00
resourceDef = new RuntimePrimitiveDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
2014-02-22 15:33:02 -05:00
}
myClassToElementDefinitions.put(theClass, resourceDef);
if (!theDatatypeDefinition.isSpecialization()) {
2015-12-30 11:27:12 -06:00
if (myVersion.isRi() && IDatatype.class.isAssignableFrom(theClass)) {
ourLog.debug("Not adding non RI type {} to RI context", theClass);
} else if (!myVersion.isRi() && !IDatatype.class.isAssignableFrom(theClass)) {
ourLog.debug("Not adding RI type {} to non RI context", theClass);
} else {
myNameToElementDefinitions.put(resourceName, resourceDef);
}
}
2014-02-22 15:33:02 -05:00
return resourceName;
}
2014-12-10 17:40:47 -05:00
private String scanResource(Class<? extends IBaseResource> theClass, ResourceDef resourceDefinition) {
2014-02-22 15:33:02 -05:00
ourLog.debug("Scanning resource class: {}", theClass.getName());
boolean primaryNameProvider = true;
2014-02-22 15:33:02 -05:00
String resourceName = resourceDefinition.name();
if (isBlank(resourceName)) {
Class<?> parent = theClass.getSuperclass();
2014-08-26 08:10:15 -04:00
primaryNameProvider = false;
while (parent.equals(Object.class) == false && isBlank(resourceName)) {
2016-02-26 18:16:35 -05:00
ResourceDef nextDef = pullAnnotation(parent, ResourceDef.class);
if (nextDef != null) {
resourceName = nextDef.name();
}
parent = parent.getSuperclass();
}
if (isBlank(resourceName)) {
2016-02-26 18:16:35 -05:00
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 ");
2014-02-22 15:33:02 -05:00
}
}
Class<? extends IBaseResource> builtInType = myNameToResourceType.get(resourceName.toLowerCase());
boolean standardType = builtInType != null && builtInType.equals(theClass) == true;
if (primaryNameProvider) {
if (builtInType != null && builtInType.equals(theClass) == false) {
primaryNameProvider = false;
}
}
String resourceId = resourceDefinition.id();
if (!isBlank(resourceId)) {
2014-03-13 17:33:12 -04:00
if (myIdToResourceDefinition.containsKey(resourceId)) {
2016-02-26 18:16:35 -05: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-08-15 15:55:39 -04:00
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(myContext, resourceName, theClass, resourceDefinition, standardType);
2014-02-22 15:33:02 -05:00
myClassToElementDefinitions.put(theClass, resourceDef);
if (primaryNameProvider) {
if (resourceDef.getStructureVersion() == myVersion) {
myNameToResourceDefinitions.put(resourceName.toLowerCase(), resourceDef);
2014-12-21 18:13:37 -05:00
}
}
scanCompositeElementForChildren(theClass, resourceDef);
2014-02-22 15:33:02 -05:00
myIdToResourceDefinition.put(resourceId, resourceDef);
scanResourceForSearchParams(theClass, resourceDef);
2014-02-22 15:33:02 -05:00
return resourceName;
2014-02-21 21:06:11 -05:00
}
2014-12-10 17:40:47 -05:00
private void scanResourceForSearchParams(Class<? extends IBaseResource> theClass, RuntimeResourceDefinition theResourceDef) {
Map<String, RuntimeSearchParam> nameToParam = new HashMap<String, RuntimeSearchParam>();
Map<Field, SearchParamDefinition> compositeFields = new LinkedHashMap<Field, SearchParamDefinition>();
for (Field nextField : theClass.getFields()) {
2016-02-26 18:16:35 -05:00
SearchParamDefinition searchParam = pullAnnotation(nextField, SearchParamDefinition.class);
if (searchParam != null) {
RestSearchParameterTypeEnum paramType = RestSearchParameterTypeEnum.forCode(searchParam.type().toLowerCase());
2014-05-28 16:07:53 -04:00
if (paramType == null) {
2015-02-13 18:01:55 -05:00
throw new ConfigurationException("Search param " + searchParam.name() + " has an invalid type: " + searchParam.type());
2014-05-13 18:59:18 -04:00
}
Set<String> providesMembershipInCompartments = null;
providesMembershipInCompartments = new HashSet<String>();
for (Compartment next : searchParam.providesMembershipIn()) {
if (paramType != RestSearchParameterTypeEnum.REFERENCE) {
2016-04-03 18:14:31 -04:00
StringBuilder b = new StringBuilder();
b.append("Search param ");
b.append(searchParam.name());
b.append(" on resource type ");
b.append(theClass.getName());
b.append(" provides compartment membership but is not of type 'reference'");
ourLog.warn(b.toString());
continue;
// throw new ConfigurationException(b.toString());
}
providesMembershipInCompartments.add(next.name());
}
if (paramType == RestSearchParameterTypeEnum.COMPOSITE) {
compositeFields.put(nextField, searchParam);
continue;
}
2016-02-26 18:16:35 -05:00
2016-05-02 11:42:55 -04:00
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType, providesMembershipInCompartments, toTargetList(searchParam.target()));
theResourceDef.addSearchParam(param);
nameToParam.put(param.getName(), param);
}
}
for (Entry<Field, SearchParamDefinition> nextEntry : compositeFields.entrySet()) {
SearchParamDefinition searchParam = nextEntry.getValue();
2014-08-15 15:55:39 -04:00
List<RuntimeSearchParam> compositeOf = new ArrayList<RuntimeSearchParam>();
2014-08-15 15:55:39 -04:00
for (String nextName : searchParam.compositeOf()) {
RuntimeSearchParam param = nameToParam.get(nextName);
2014-08-15 15:55:39 -04:00
if (param == null) {
2016-02-26 18:16:35 -05:00
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);
}
2016-05-02 11:42:55 -04:00
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), RestSearchParameterTypeEnum.COMPOSITE, compositeOf, null, toTargetList(searchParam.target()));
theResourceDef.addSearchParam(param);
}
}
2016-05-02 11:42:55 -04:00
private Set<String> toTargetList(Class<? extends IBaseResource>[] theTarget) {
HashSet<String> retVal = new HashSet<String>();
for (Class<? extends IBaseResource> nextType : theTarget) {
ResourceDef resourceDef = nextType.getAnnotation(ResourceDef.class);
if (resourceDef != null) {
retVal.add(resourceDef.name());
}
}
return retVal;
}
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;
}
2015-05-02 14:04:43 -07:00
static Set<Class<? extends IBase>> scanVersionPropertyFile(Set<Class<? extends IBase>> theDatatypes, Map<String, Class<? extends IBaseResource>> theResourceTypes, FhirVersionEnum version) {
Set<Class<? extends IBase>> retVal = new HashSet<Class<? extends IBase>>();
InputStream str = version.getVersionImplementation().getFhirVersionPropertiesFile();
Properties prop = new Properties();
try {
prop.load(str);
for (Entry<Object, Object> nextEntry : prop.entrySet()) {
String nextKey = nextEntry.getKey().toString();
String nextValue = nextEntry.getValue().toString();
if (nextKey.startsWith("datatype.")) {
if (theDatatypes != null) {
try {
// Datatypes
2015-05-02 14:04:43 -07:00
@SuppressWarnings("unchecked")
Class<? extends IBase> dtType = (Class<? extends IBase>) Class.forName(nextValue);
retVal.add(dtType);
2015-02-16 11:33:46 -05:00
if (IElement.class.isAssignableFrom(dtType)) {
@SuppressWarnings("unchecked")
Class<? extends IElement> nextClass = (Class<? extends IElement>) dtType;
theDatatypes.add(nextClass);
} else if (IBaseDatatype.class.isAssignableFrom(dtType)) {
@SuppressWarnings("unchecked")
Class<? extends IBaseDatatype> nextClass = (Class<? extends IBaseDatatype>) dtType;
theDatatypes.add(nextClass);
} else {
ourLog.warn("Class is not assignable from " + IElement.class.getSimpleName() + " or " + IBaseDatatype.class.getSimpleName() + ": " + nextValue);
continue;
}
} catch (ClassNotFoundException e) {
2015-05-05 18:16:22 -04:00
throw new ConfigurationException("Unknown class[" + nextValue + "] for data type definition: " + nextKey.substring("datatype.".length()), e);
}
}
} else if (nextKey.startsWith("resource.")) {
// Resources
String resName = nextKey.substring("resource.".length()).toLowerCase();
try {
@SuppressWarnings("unchecked")
Class<? extends IBaseResource> nextClass = (Class<? extends IBaseResource>) Class.forName(nextValue);
if (!IBaseResource.class.isAssignableFrom(nextClass)) {
2015-05-05 18:16:22 -04:00
throw new ConfigurationException("Class is not assignable from " + IBaseResource.class.getSimpleName() + ": " + nextValue);
}
theResourceTypes.put(resName, nextClass);
} catch (ClassNotFoundException e) {
2015-05-05 18:16:22 -04:00
throw new ConfigurationException("Unknown class[" + nextValue + "] for resource definition: " + nextKey.substring("resource.".length()), e);
}
} else {
2015-05-05 18:16:22 -04:00
throw new ConfigurationException("Unexpected property in version property file: " + nextKey + "=" + nextValue);
}
}
} catch (IOException e) {
throw new ConfigurationException("Failed to load model property file from classpath: " + "/ca/uhn/fhir/model/dstu/model.properties");
}
return retVal;
2015-04-20 07:31:45 -04:00
}
2014-02-17 17:58:32 -05:00
}