2014-02-17 17:58:32 -05:00
package ca.uhn.fhir.context ;
2014-04-24 12:25:47 -04:00
/ *
* # % L
2014-07-21 09:24:38 -04:00
* HAPI FHIR - Core Library
2014-04-24 12:25:47 -04:00
* % %
2015-01-09 16:15:55 -05:00
* Copyright ( C ) 2014 - 2015 University Health Network
2014-04-24 12:25:47 -04:00
* % %
* 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 %
* /
2015-02-18 17:00:47 -05:00
import static org.apache.commons.lang3.StringUtils.isBlank ;
2014-02-17 17:58:32 -05:00
2014-05-07 08:18:43 -04: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 ;
2014-12-10 17:40:47 -05:00
import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Method ;
2014-09-26 10:58:19 -04:00
import java.lang.reflect.Modifier ;
2014-02-19 13:02:51 -05:00
import java.lang.reflect.ParameterizedType ;
2014-12-10 17:40:47 -05:00
import java.lang.reflect.Proxy ;
2014-03-03 09:05:55 -05:00
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-07-29 09:10:25 -04:00
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 ;
2014-05-07 08:18:43 -04:00
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
2014-12-10 14:28:25 -05:00
import org.hl7.fhir.instance.model.IBase ;
import org.hl7.fhir.instance.model.IBaseResource ;
import org.hl7.fhir.instance.model.ICompositeType ;
2014-12-10 17:40:47 -05:00
import org.hl7.fhir.instance.model.IPrimitiveType ;
2015-02-16 11:33:46 -05:00
import org.hl7.fhir.instance.model.api.IAnyResource ;
import org.hl7.fhir.instance.model.api.IBackboneElement ;
import org.hl7.fhir.instance.model.api.IBaseDatatype ;
2015-02-20 17:34:13 -05:00
import org.hl7.fhir.instance.model.api.IBaseEnumFactory ;
2015-02-16 11:33:46 -05:00
import org.hl7.fhir.instance.model.api.IBaseExtension ;
import org.hl7.fhir.instance.model.api.IDatatypeElement ;
import org.hl7.fhir.instance.model.api.IDomainResource ;
import org.hl7.fhir.instance.model.api.INarrative ;
import org.hl7.fhir.instance.model.api.IReference ;
2014-12-10 14:28:25 -05:00
2014-02-17 17:58:32 -05:00
import ca.uhn.fhir.model.api.CodeableConceptElement ;
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.ICodeEnum ;
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 ;
2014-03-03 09:05:55 -05:00
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-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-05-05 05:57:43 -07:00
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition ;
2015-02-12 17:32:00 -05:00
import ca.uhn.fhir.model.base.composite.BaseContainedDt ;
import ca.uhn.fhir.model.base.composite.BaseNarrativeDt ;
2014-07-17 09:53:52 -04:00
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt ;
2014-05-13 18:59:18 -04:00
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum ;
2014-03-03 09:05:55 -05:00
import ca.uhn.fhir.model.primitive.BoundCodeDt ;
2014-02-24 11:46:08 -05:00
import ca.uhn.fhir.model.primitive.ICodedDatatype ;
import ca.uhn.fhir.model.primitive.XhtmlDt ;
2014-03-16 15:09:30 -04:00
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 < ? > > ( ) ;
2015-02-14 13:27:11 -05:00
private FhirContext myContext ;
2014-03-11 08:39:55 -04:00
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 > ( ) ;
2015-02-14 13:27:11 -05:00
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 > > ( ) ;
2014-03-11 08:39:55 -04:00
private Set < Class < ? extends ICodeEnum > > myScanAlsoCodeTable = new HashSet < Class < ? extends ICodeEnum > > ( ) ;
2015-02-14 13:27:11 -05:00
private FhirVersionEnum myVersion ;
2014-03-11 08:39:55 -04:00
2015-02-14 13:27:11 -05:00
ModelScanner ( FhirContext theContext , FhirVersionEnum theVersion , Map < Class < ? extends IBase > , BaseRuntimeElementDefinition < ? > > theExistingDefinitions , Collection < Class < ? extends IElement > > theResourceTypes ) throws ConfigurationException {
2014-12-01 08:13:32 -05:00
myContext = theContext ;
2015-02-14 13:27:11 -05:00
myVersion = theVersion ;
2014-12-11 16:07:00 -05:00
Set < Class < ? extends IBase > > toScan ;
2014-12-01 08:13:32 -05:00
if ( theResourceTypes ! = null ) {
2014-12-11 16:07:00 -05:00
toScan = new HashSet < Class < ? extends IBase > > ( theResourceTypes ) ;
2014-12-01 08:13:32 -05:00
} else {
2014-12-11 16:07:00 -05:00
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 ( ) ) ) {
2014-03-11 08:39:55 -04:00
return ;
}
myScanAlso . add ( theType ) ;
}
private Class < ? > determineElementType ( Field next ) {
Class < ? > nextElementType = next . getType ( ) ;
if ( List . class . equals ( nextElementType ) ) {
2014-03-16 15:09:30 -04:00
nextElementType = ReflectionUtil . getGenericCollectionTypeOfField ( next ) ;
2014-03-11 08:39:55 -04:00
} 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 ) ;
}
}
2015-02-14 13:27:11 -05:00
public Map < Class < ? extends IBase > , BaseRuntimeElementDefinition < ? > > getClassToElementDefinitions ( ) {
return myClassToElementDefinitions ;
}
public Map < String , RuntimeResourceDefinition > getIdToResourceDefinition ( ) {
return myIdToResourceDefinition ;
}
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 ( ) ;
2014-05-07 08:18:43 -04:00
long start = System . currentTimeMillis ( ) ;
2015-02-14 13:27:11 -05:00
Map < String , Class < ? extends IBaseResource > > resourceTypes = myNameToResourceType ;
2014-05-07 08:18:43 -04:00
2015-02-14 13:27:11 -05:00
scanVersionPropertyFile ( theDatatypes , resourceTypes , myVersion ) ;
2014-05-07 08:18:43 -04: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-05-07 08:18:43 -04:00
2014-02-18 18:10:50 -05:00
do {
2015-02-14 13:27:11 -05:00
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 ( ) ;
}
}
2015-02-14 13:27:11 -05:00
theDatatypes . clear ( ) ;
theDatatypes . addAll ( myScanAlso ) ;
2014-02-18 18:10:50 -05:00
myScanAlso . clear ( ) ;
2015-02-14 13:27:11 -05:00
} 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 ( ) ;
2015-02-12 17:32:00 -05:00
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 ( ) ;
2015-02-12 17:32:00 -05:00
myRuntimeChildUndeclaredExtensionDefinition . sealAndInitialize ( myContext , myClassToElementDefinitions ) ;
2014-03-03 09:05:55 -05:00
2014-05-07 08:18:43 -04:00
long time = System . currentTimeMillis ( ) - start ;
2014-07-04 09:27:36 -04:00
int size = myClassToElementDefinitions . size ( ) - startSize ;
2015-02-23 09:08:59 -05:00
ourLog . debug ( " Done scanning FHIR library, found {} model entries in {}ms " , size , time ) ;
2014-02-17 17:58:32 -05:00
}
2015-02-14 13:27:11 -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 .
* /
private < T extends Annotation > T pullAnnotation ( AnnotatedElement theTarget , Class < T > theAnnotationType ) {
2015-03-04 13:50:58 -05:00
2015-02-14 13:27:11 -05:00
T retVal = theTarget . getAnnotation ( theAnnotationType ) ;
2015-03-04 13:50:58 -05:00
if ( true ) {
return retVal ;
}
// Below disabled for now due to performance issues
/ *
2015-02-14 13:27:11 -05:00
if ( retVal = = null ) {
String sourceClassName = theAnnotationType . getName ( ) ;
String candidateAltClassName = sourceClassName . replace ( " ca.uhn.fhir.model.api.annotation " , " org.hl7.fhir.instance.model.annotations " ) ;
if ( ! sourceClassName . equals ( candidateAltClassName ) ) {
try {
final Class < ? extends Annotation > altAnnotationClass = ( Class < ? extends Annotation > ) Class . forName ( candidateAltClassName ) ;
final Annotation altAnnotation = theTarget . getAnnotation ( altAnnotationClass ) ;
if ( altAnnotation = = null ) {
return null ;
}
ourLog . debug ( " Forwarding annotation request for [{}] to class [{}] " , sourceClassName , candidateAltClassName ) ;
InvocationHandler h = new InvocationHandler ( ) {
@Override
public Object invoke ( Object theProxy , Method theMethod , Object [ ] theArgs ) throws Throwable {
Method altMethod = altAnnotationClass . getMethod ( theMethod . getName ( ) , theMethod . getParameterTypes ( ) ) ;
return altMethod . invoke ( altAnnotation , theArgs ) ;
}
} ;
retVal = ( T ) Proxy . newProxyInstance ( theAnnotationType . getClassLoader ( ) , new Class < ? > [ ] { theAnnotationType } , h ) ;
} catch ( ClassNotFoundException e ) {
return null ;
}
}
}
2015-03-04 13:50:58 -05:00
* /
2015-02-14 13:27:11 -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
}
2014-12-22 12:03:02 -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 ) ) {
2015-01-24 07:42:14 +01: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
}
2014-12-22 12:03:02 -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 {
2015-01-24 07:42:14 +01: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-12-22 12:03:02 -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-02-16 11:33:46 -05:00
if ( IResourceBlock . class . isAssignableFrom ( theClass ) | | IBackboneElement . class . isAssignableFrom ( theClass ) | | IDatatypeElement . class . isAssignableFrom ( theClass ) ) {
scanBlock ( theClass ) ;
2014-02-21 21:06:11 -05:00
} else {
2015-01-24 07:42:14 +01: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 ) {
2014-12-04 11:04:53 -05:00
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 ( ) ) ;
}
RuntimeResourceBlockDefinition resourceDef = new RuntimeResourceBlockDefinition ( resourceName , theClass ) ;
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
2014-03-24 08:32:28 -04:00
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 ) ) {
resourceDef = new RuntimeExtensionDtDefinition ( theDatatypeDefinition , theClass ) ;
} else {
resourceDef = new RuntimeCompositeDatatypeDefinition ( theDatatypeDefinition , theClass ) ;
}
2014-02-18 18:10:50 -05:00
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
2014-03-24 08:32:28 -04:00
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 09:19:07 -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-08-15 09:19:07 -04:00
* /
2014-12-10 17:40:47 -05:00
Class < ? extends IBase > current = theClass ;
2014-02-19 11:59:12 -05:00
do {
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 ) {
2014-03-25 13:42:38 -04:00
scanCompositeElementForChildren ( next , elementNames , orderToElementDef , orderToExtensionDef ) ;
2014-02-19 11:59:12 -05:00
}
2014-03-03 09:05:55 -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-03-03 09:05:55 -05:00
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 ) {
2014-03-03 09:05:55 -05:00
theDefinition . addExtension ( ( RuntimeChildDeclaredExtensionDefinition ) nextExt ) ;
2014-02-19 11:59:12 -05:00
}
}
}
2014-02-19 13:02:51 -05:00
@SuppressWarnings ( " unchecked " )
2015-01-24 07:42:14 +01:00
private void scanCompositeElementForChildren ( Class < ? extends IBase > theClass , Set < String > elementNames , TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > theOrderToElementDef , TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > theOrderToExtensionDef ) {
2014-02-28 13:27:35 -05:00
int baseElementOrder = theOrderToElementDef . isEmpty ( ) ? 0 : theOrderToElementDef . lastEntry ( ) . getKey ( ) + 1 ;
2014-03-03 09:05:55 -05:00
2014-02-19 11:59:12 -05:00
for ( Field next : theClass . getDeclaredFields ( ) ) {
2014-02-19 17:33:46 -05:00
2014-09-26 10:58:19 -04:00
if ( Modifier . isFinal ( next . getModifiers ( ) ) ) {
2014-12-01 08:13:32 -05:00
ourLog . trace ( " Ignoring constant {} on target type {} " , next . getName ( ) , theClass ) ;
2014-09-26 10:58:19 -04:00
continue ;
}
2014-12-22 12:03:02 -05:00
2014-12-10 17:40:47 -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 ;
}
2014-03-11 08:39:55 -04:00
2014-12-22 12:03:02 -05:00
Description descriptionAnnotation = pullAnnotation ( next , Description . class ) ;
2014-02-17 17:58:32 -05:00
2014-08-15 09:19:07 -04:00
TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > orderMap = theOrderToElementDef ;
2014-12-22 12:03:02 -05:00
Extension extensionAttr = pullAnnotation ( next , Extension . class ) ;
2014-08-15 09:19:07 -04:00
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 ( ) ;
2014-03-24 08:32:28 -04:00
int order = childAnnotation . order ( ) ;
2014-08-15 09:19:07 -04:00
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 ) {
2015-01-24 07:42:14 +01: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 ( ) ;
orderMap . remove ( nextEntry . getKey ( ) ) ;
elementNames . remove ( elementName ) ;
break ;
}
}
if ( order = = Child . REPLACE_PARENT ) {
2015-01-24 07:42:14 +01: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 09:19:07 -04:00
}
2014-08-15 15:55:39 -04:00
2014-08-15 09:19:07 -04:00
}
2014-03-24 08:32:28 -04:00
if ( order < 0 & & order ! = Child . ORDER_UNKNOWN ) {
2014-05-07 08:18:43 -04:00
throw new ConfigurationException ( " Invalid order ' " + order + " ' on @Child for field ' " + next . getName ( ) + " ' on target type: " + theClass ) ;
2014-03-24 08:32:28 -04:00
}
if ( order ! = Child . ORDER_UNKNOWN ) {
order = order + baseElementOrder ;
}
2015-02-16 11:33:46 -05:00
// int min = childAnnotation.min();
// int max = childAnnotation.max();
2015-01-24 07:42:14 +01:00
2014-02-20 12:13:05 -05:00
/ *
2015-01-24 07:42:14 +01: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
* /
2015-01-24 07:42:14 +01:00
if ( order = = Child . ORDER_UNKNOWN ) {
order = Integer . MIN_VALUE ;
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 ) ) {
2015-01-24 07:42:14 +01: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
2015-02-16 23:02:50 -05:00
if ( IAnyResource . class . isAssignableFrom ( nextElementType ) | | IResource . class . equals ( nextElementType ) ) {
2015-02-16 11:33:46 -05:00
/ *
* Child is a resource as a direct child , as in Bundle . entry . resource
* /
RuntimeChildDirectResource def = new RuntimeChildDirectResource ( next , childAnnotation , descriptionAnnotation , elementName ) ;
orderMap . put ( order , def ) ;
} else if ( BaseContainedDt . class . isAssignableFrom ( nextElementType ) | | ( childAnnotation . name ( ) . equals ( " contained " ) & & IDomainResource . class . isAssignableFrom ( theClass ) ) ) {
2014-03-17 08:57:57 -04:00
/ *
* Child is contained resources
* /
RuntimeChildContainedResources def = new RuntimeChildContainedResources ( next , childAnnotation , descriptionAnnotation , elementName ) ;
orderMap . put ( order , def ) ;
2014-05-07 08:18:43 -04:00
2015-02-16 11:33:46 -05:00
} else if ( choiceTypes . size ( ) > 1 & & ! BaseResourceReferenceDt . class . isAssignableFrom ( nextElementType ) & & ! IReference . class . isAssignableFrom ( nextElementType ) ) {
2014-02-18 08:26:49 -05:00
/ *
* Child is a choice element
* /
2014-12-10 17:40:47 -05:00
for ( Class < ? extends IBase > nextType : choiceTypes ) {
2014-02-21 21:06:11 -05:00
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-05-28 16:07:53 -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-02-23 18:13:59 -05:00
} else if ( extensionAttr ! = null ) {
/ *
* Child is an extension
* /
2014-12-10 17:40:47 -05:00
Class < ? extends IBase > et = ( Class < ? extends IBase > ) nextElementType ;
2015-01-24 07:42:14 +01: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 ) ;
2014-03-03 09:05:55 -05:00
if ( IElement . class . isAssignableFrom ( nextElementType ) ) {
2014-02-23 18:13:59 -05:00
addScanAlso ( ( Class < ? extends IElement > ) nextElementType ) ;
}
2015-02-16 11:33:46 -05:00
} else if ( BaseResourceReferenceDt . class . isAssignableFrom ( nextElementType ) | | IReference . class . isAssignableFrom ( nextElementType ) ) {
2014-02-18 08:26:49 -05:00
/ *
* Child is a resource reference
* /
2014-12-10 17:40:47 -05:00
List < Class < ? extends IBaseResource > > refTypesList = new ArrayList < Class < ? extends IBaseResource > > ( ) ;
2014-03-10 12:43:49 -04:00
for ( Class < ? extends IElement > nextType : childAnnotation . type ( ) ) {
2014-12-10 17:40:47 -05:00
if ( IBaseResource . class . isAssignableFrom ( nextType ) = = false ) {
2015-01-24 07:42:14 +01:00
throw new ConfigurationException ( " Field ' " + next . getName ( ) + " ' in class ' " + next . getDeclaringClass ( ) . getCanonicalName ( ) + " ' is of type " + BaseResourceReferenceDt . class + " but contains a non-resource type: " + nextType . getCanonicalName ( ) ) ;
2014-03-10 12:43:49 -04:00
}
2014-12-10 17:40:47 -05:00
refTypesList . add ( ( Class < ? extends IBaseResource > ) 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
2015-02-16 11:33:46 -05:00
} else if ( IResourceBlock . class . isAssignableFrom ( nextElementType ) | | IBackboneElement . class . isAssignableFrom ( nextElementType ) | | IDatatypeElement . class . isAssignableFrom ( nextElementType ) ) {
2014-02-20 12:13:05 -05:00
/ *
2015-01-24 07:42:14 +01: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
2014-12-10 17:40:47 -05:00
Class < ? extends IBase > blockDef = ( Class < ? extends IBase > ) nextElementType ;
2014-02-21 21:06:11 -05:00
addScanAlso ( blockDef ) ;
2014-03-11 08:39:55 -04:00
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
2015-02-20 17:34:13 -05:00
} else if ( IDatatype . class . equals ( nextElementType ) | | IElement . class . equals ( nextElementType ) | | " org.hl7.fhir.instance.model.Type " . equals ( nextElementType . getName ( ) ) | | IBaseDatatype . class . equals ( nextElementType ) ) {
2014-05-07 08:18:43 -04:00
2014-03-17 08:57:57 -04:00
RuntimeChildAny def = new RuntimeChildAny ( next , elementName , childAnnotation , descriptionAnnotation ) ;
orderMap . put ( order , def ) ;
2015-02-16 11:33:46 -05:00
} else if ( IDatatype . class . isAssignableFrom ( nextElementType ) | | IPrimitiveType . class . isAssignableFrom ( nextElementType ) | | ICompositeType . class . isAssignableFrom ( nextElementType ) | | IBaseDatatype . class . isAssignableFrom ( nextElementType ) | | IBaseExtension . class . isAssignableFrom ( nextElementType ) ) {
2014-12-10 17:40:47 -05:00
Class < ? extends IBase > nextDatatype = ( Class < ? extends IBase > ) 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 ;
2015-02-20 17:34:13 -05:00
if ( IPrimitiveType . class . isAssignableFrom ( nextElementType ) ) {
2014-03-03 09:05:55 -05:00
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 ) ;
2015-02-20 17:34:13 -05:00
} else if ( childAnnotation . enumFactory ( ) . getSimpleName ( ) . equals ( " NoEnumFactory " ) = = false ) {
Class < ? extends IBaseEnumFactory < ? > > enumFactory = childAnnotation . enumFactory ( ) ;
def = new RuntimeChildEnumerationDatatypeDefinition ( next , elementName , childAnnotation , descriptionAnnotation , nextDatatype , enumFactory ) ;
2014-03-03 09:05:55 -05:00
} else {
2014-03-10 12:43:49 -04:00
def = new RuntimeChildPrimitiveDatatypeDefinition ( next , elementName , descriptionAnnotation , childAnnotation , nextDatatype ) ;
2014-03-03 09:05:55 -05:00
}
2014-02-18 18:10:50 -05:00
} else {
2014-10-16 13:41:57 -04:00
if ( IBoundCodeableConcept . class . isAssignableFrom ( nextElementType ) ) {
2014-03-10 18:17:30 -04:00
IValueSetEnumBinder < Enum < ? > > binder = getBoundCodeBinder ( next ) ;
def = new RuntimeChildCompositeBoundDatatypeDefinition ( next , elementName , childAnnotation , descriptionAnnotation , nextDatatype , binder ) ;
2015-02-16 11:33:46 -05:00
} else if ( BaseNarrativeDt . class . isAssignableFrom ( nextElementType ) | | INarrative . class . getName ( ) . equals ( nextElementType . getClass ( ) . getName ( ) ) ) {
2014-03-24 08:32:28 -04:00
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
}
2014-12-22 12:03:02 -05:00
CodeableConceptElement concept = pullAnnotation ( next , CodeableConceptElement . class ) ;
2014-02-18 18:10:50 -05:00
if ( concept ! = null ) {
if ( ! ICodedDatatype . class . isAssignableFrom ( nextDatatype ) ) {
2015-01-24 07:42:14 +01: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 {
2014-03-03 09:05:55 -05:00
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 ) ;
}
}
2015-02-12 17:32:00 -05:00
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 ;
resourceDef = new RuntimePrimitiveDatatypeNarrativeDefinition ( resourceName , clazz ) ;
} else {
2014-03-16 15:09:30 -04:00
resourceDef = new RuntimePrimitiveDatatypeDefinition ( theDatatypeDefinition , theClass ) ;
2014-02-22 15:33:02 -05:00
}
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
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 ( ) ) ;
2014-08-16 17:46:30 -04:00
boolean primaryNameProvider = true ;
2014-02-22 15:33:02 -05:00
String resourceName = resourceDefinition . name ( ) ;
if ( isBlank ( resourceName ) ) {
2014-08-16 17:46:30 -04:00
Class < ? > parent = theClass . getSuperclass ( ) ;
2014-08-26 08:10:15 -04:00
primaryNameProvider = false ;
while ( parent . equals ( Object . class ) = = false & & isBlank ( resourceName ) ) {
2014-12-22 12:03:02 -05:00
ResourceDef nextDef = pullAnnotation ( parent , ResourceDef . class ) ;
2014-08-16 17:46:30 -04:00
if ( nextDef ! = null ) {
resourceName = nextDef . name ( ) ;
}
parent = parent . getSuperclass ( ) ;
}
if ( isBlank ( resourceName ) ) {
2015-01-24 07:42:14 +01: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
}
}
2014-03-11 08:39:55 -04:00
String resourceId = resourceDefinition . id ( ) ;
2015-02-14 13:27:11 -05:00
if ( ! isBlank ( resourceId ) ) {
2014-03-13 17:33:12 -04:00
if ( myIdToResourceDefinition . containsKey ( resourceId ) ) {
2015-01-24 07:42:14 +01: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-11 08:39:55 -04:00
}
2014-08-15 15:55:39 -04:00
2014-10-02 09:54:52 -04:00
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition ( myContext , resourceName , theClass , resourceDefinition ) ;
2014-02-22 15:33:02 -05:00
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
2014-08-16 17:46:30 -04:00
if ( primaryNameProvider ) {
2015-02-14 13:27:11 -05:00
if ( resourceDef . getStructureVersion ( ) = = myVersion ) {
2014-12-22 12:03:02 -05:00
myNameToResourceDefinitions . put ( resourceName , resourceDef ) ;
2014-12-21 18:13:37 -05:00
}
2014-08-16 17:46:30 -04:00
}
2014-03-24 08:32:28 -04:00
scanCompositeElementForChildren ( theClass , resourceDef ) ;
2014-02-22 15:33:02 -05:00
2014-03-11 08:39:55 -04:00
myIdToResourceDefinition . put ( resourceId , resourceDef ) ;
2014-05-07 08:18:43 -04:00
2014-05-05 05:57:43 -07:00
scanResourceForSearchParams ( theClass , resourceDef ) ;
2014-05-07 08:18:43 -04:00
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 ) {
2014-05-07 08:18:43 -04:00
2014-07-29 09:10:25 -04:00
Map < String , RuntimeSearchParam > nameToParam = new HashMap < String , RuntimeSearchParam > ( ) ;
Map < Field , SearchParamDefinition > compositeFields = new LinkedHashMap < Field , SearchParamDefinition > ( ) ;
2014-05-05 05:57:43 -07:00
for ( Field nextField : theClass . getFields ( ) ) {
2014-12-22 12:03:02 -05:00
SearchParamDefinition searchParam = pullAnnotation ( nextField , SearchParamDefinition . class ) ;
2014-05-05 05:57:43 -07:00
if ( searchParam ! = null ) {
2014-05-13 18:59:18 -04:00
SearchParamTypeEnum paramType = SearchParamTypeEnum . valueOf ( searchParam . type ( ) . toUpperCase ( ) ) ;
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
}
2014-08-15 15:55:39 -04:00
if ( paramType = = SearchParamTypeEnum . COMPOSITE ) {
2014-07-29 09:10:25 -04:00
compositeFields . put ( nextField , searchParam ) ;
continue ;
}
2014-05-13 18:59:18 -04:00
RuntimeSearchParam param = new RuntimeSearchParam ( searchParam . name ( ) , searchParam . description ( ) , searchParam . path ( ) , paramType ) ;
2014-05-05 05:57:43 -07:00
theResourceDef . addSearchParam ( param ) ;
2014-07-29 09:10:25 -04:00
nameToParam . put ( param . getName ( ) , param ) ;
2014-05-05 05:57:43 -07:00
}
}
2014-05-07 08:18:43 -04:00
2014-07-29 09:10:25 -04:00
for ( Entry < Field , SearchParamDefinition > nextEntry : compositeFields . entrySet ( ) ) {
SearchParamDefinition searchParam = nextEntry . getValue ( ) ;
2014-08-15 15:55:39 -04:00
2014-07-29 09:10:25 -04:00
List < RuntimeSearchParam > compositeOf = new ArrayList < RuntimeSearchParam > ( ) ;
2014-08-15 15:55:39 -04:00
for ( String nextName : searchParam . compositeOf ( ) ) {
2014-07-29 09:10:25 -04:00
RuntimeSearchParam param = nameToParam . get ( nextName ) ;
2014-08-15 15:55:39 -04:00
if ( param = = null ) {
2015-01-24 07:42:14 +01: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 ( ) } ) ;
2014-07-29 09:10:25 -04:00
continue ;
}
compositeOf . add ( param ) ;
}
RuntimeSearchParam param = new RuntimeSearchParam ( searchParam . name ( ) , searchParam . description ( ) , searchParam . path ( ) , SearchParamTypeEnum . COMPOSITE , compositeOf ) ;
theResourceDef . addSearchParam ( param ) ;
}
2014-05-05 05:57:43 -07:00
}
2014-03-11 08:39:55 -04:00
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-02-14 13:27:11 -05:00
static void scanVersionPropertyFile ( Set < Class < ? extends IBase > > theDatatypes , Map < String , Class < ? extends IBaseResource > > theResourceTypes , FhirVersionEnum version ) {
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-02-16 11:33:46 -05:00
Class < ? > dtType = Class . forName ( nextValue ) ;
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 ) ;
2015-02-14 13:27:11 -05:00
continue ;
}
} catch ( ClassNotFoundException e ) {
2015-02-16 11:33:46 -05:00
ourLog . error ( " Unknown class[ " + nextValue + " ] for data type definition: " + nextKey . substring ( " datatype. " . length ( ) ) , e ) ;
2015-02-14 13:27:11 -05:00
}
}
} 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 ) ) {
ourLog . warn ( " Class is not assignable from " + IBaseResource . class . getSimpleName ( ) + " : " + nextValue ) ;
continue ;
}
theResourceTypes . put ( resName , nextClass ) ;
} catch ( ClassNotFoundException e ) {
2015-02-16 11:33:46 -05:00
ourLog . error ( " Unknown class[ " + nextValue + " ] for resource definition: " + nextKey . substring ( " resource. " . length ( ) ) , e ) ;
2015-02-14 13:27:11 -05:00
}
} else {
ourLog . warn ( " 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 " ) ;
}
}
2014-02-17 17:58:32 -05:00
}