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
* % %
2016-01-07 08:50:07 -05:00
* Copyright ( C ) 2014 - 2016 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-08-20 15:27:24 -04: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-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-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
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 ;
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 ;
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 ;
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-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.XhtmlDt ;
2015-03-24 18:39:19 -04:00
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum ;
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 > ( ) ;
2015-05-31 16:52:31 -04:00
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 > ( ) ;
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 > > ( ) ;
2015-02-14 13:27:11 -05:00
private FhirVersionEnum myVersion ;
2015-05-31 16:52:31 -04:00
2015-05-02 14:04:43 -07:00
private Set < Class < ? extends IBase > > myVersionTypes ;
2014-03-11 08:39:55 -04:00
2016-02-26 18:16:35 -05:00
ModelScanner ( FhirContext theContext , FhirVersionEnum theVersion , Map < Class < ? extends IBase > , BaseRuntimeElementDefinition < ? > > theExistingDefinitions ,
2016-05-21 14:35:10 -04:00
Collection < Class < ? extends IBase > > 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 ;
}
2015-05-31 16:52:31 -04:00
public Map < String , BaseRuntimeElementDefinition < ? > > getNameToElementDefinitions ( ) {
return myNameToElementDefinitions ;
}
public Map < String , RuntimeResourceDefinition > getNameToResourceDefinition ( ) {
return myNameToResourceDefinitions ;
}
2015-02-14 13:27:11 -05:00
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-05-02 14:04:43 -07:00
myVersionTypes = scanVersionPropertyFile ( theDatatypes , resourceTypes , myVersion ) ;
2016-02-26 18:16:35 -05:00
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-05-31 16:52:31 -04:00
private boolean isStandardType ( Class < ? extends IBase > theClass ) {
2015-12-30 11:27:12 -06:00
boolean retVal = myVersionTypes . contains ( theClass ) ;
return retVal ;
2015-05-31 16:52:31 -04:00
}
2015-02-14 13:27:11 -05:00
/ * *
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 .
2015-02-14 13:27:11 -05:00
* /
2016-02-26 18:16:35 -05:00
private < T extends Annotation > T pullAnnotation ( AnnotatedElement theTarget , Class < T > theAnnotationType ) {
2015-02-14 13:27:11 -05:00
T retVal = theTarget . getAnnotation ( theAnnotationType ) ;
2016-02-26 18:16:35 -05:00
return retVal ;
2015-02-14 13:27:11 -05:00
}
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 {
2015-09-08 14:00:11 -04:00
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 ) {
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 ( ) ) ;
}
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 ) ;
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 ) ) {
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 ) ;
2016-04-25 07:19:31 -04:00
myNameToElementDefinitions . put ( resourceDef . getName ( ) . toLowerCase ( ) , 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 ;
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 ;
}
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 " )
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-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
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 ;
}
2014-03-11 08:39:55 -04:00
2016-02-26 18:16:35 -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 ;
2016-02-26 18:16:35 -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 ( ) ;
2016-04-28 13:25:20 -04:00
boolean childIsChoiceType = false ;
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 ) {
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 ( ) ;
2016-04-28 13:25:20 -04:00
BaseRuntimeDeclaredChildDefinition existing = orderMap . remove ( nextEntry . getKey ( ) ) ;
2014-08-15 15:55:39 -04:00
elementNames . remove ( elementName ) ;
2016-04-28 13:25:20 -04:00
/ *
* 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 09:19:07 -04:00
}
2014-08-15 15:55:39 -04:00
2014-08-15 09:19:07 -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 ;
2014-03-24 08:32:28 -04:00
}
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
/ *
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
* /
2015-01-24 07:42:14 +01:00
if ( order = = Child . ORDER_UNKNOWN ) {
2016-05-21 14:35:10 -04:00
order = Integer . valueOf ( 0 ) ;
2015-01-24 07:42:14 +01:00
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
2016-04-11 08:36:12 -04: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 ) ;
2014-05-07 08:18:43 -04:00
2016-04-28 13:25:20 -04:00
} 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
2016-04-28 13:25:20 -04:00
} else if ( next . getType ( ) . equals ( ExtensionDt . class ) ) {
2015-05-31 16:52:31 -04:00
2016-04-28 13:25:20 -04:00
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
2016-04-28 13:25:20 -04: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
2016-04-28 13:25:20 -04:00
Object binder = null ;
if ( BoundCodeDt . class . isAssignableFrom ( nextElementType ) | | IBoundCodeableConcept . class . isAssignableFrom ( nextElementType ) ) {
binder = getBoundCodeBinder ( next ) ;
}
2014-02-21 21:06:11 -05:00
2016-04-28 13:25:20 -04:00
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition ( next , childAnnotation , descriptionAnnotation , extensionAttr , elementName , extensionAttr . url ( ) , et ,
binder ) ;
2014-05-07 08:18:43 -04:00
2016-04-28 13:25:20 -04:00
if ( IBaseEnumeration . class . isAssignableFrom ( nextElementType ) ) {
def . setEnumerationType ( ReflectionUtil . getGenericCollectionTypeOfFieldWithSecondOrderForList ( next ) ) ;
}
2014-03-17 08:57:57 -04:00
2016-04-28 13:25:20 -04:00
orderMap . put ( order , def ) ;
if ( IBase . class . isAssignableFrom ( nextElementType ) ) {
addScanAlso ( ( Class < ? extends IBase > ) nextElementType ) ;
2014-03-03 09:05:55 -05:00
}
2016-04-28 13:25:20 -04:00
} 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 {
2016-04-28 13:25:20 -04:00
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
2016-04-28 13:25:20 -04:00
orderMap . put ( order , def ) ;
2014-02-18 18:10:50 -05:00
2016-04-28 13:25:20 -04: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 ;
}
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 ;
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 ) ;
2015-07-05 17:14:15 -04:00
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 ) ;
}
2015-07-05 17:14:15 -04:00
}
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 ( ) ) ;
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 ) ) {
2016-02-26 18:16:35 -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 ) ) {
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
}
}
2016-03-22 11:46:08 +01: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 ;
}
}
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 ) ) {
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-03-11 08:39:55 -04:00
}
2014-08-15 15:55:39 -04:00
2016-03-22 11:46:08 +01:00
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition ( myContext , resourceName , theClass , resourceDefinition , standardType ) ;
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 ) {
2016-04-25 07:19:31 -04:00
myNameToResourceDefinitions . put ( resourceName . toLowerCase ( ) , 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 ( ) ) {
2016-02-26 18:16:35 -05:00
SearchParamDefinition searchParam = pullAnnotation ( nextField , SearchParamDefinition . class ) ;
2014-05-05 05:57:43 -07:00
if ( searchParam ! = null ) {
2016-03-14 06:31:45 -04:00
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
}
2016-03-14 06:31:45 -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());
2016-03-14 06:31:45 -04:00
}
providesMembershipInCompartments . add ( next . name ( ) ) ;
}
2015-03-24 18:39:19 -04:00
if ( paramType = = RestSearchParameterTypeEnum . COMPOSITE ) {
2014-07-29 09:10:25 -04:00
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 ( ) ) ) ;
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 ) {
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 ( ) } ) ;
2014-07-29 09:10:25 -04:00
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 ( ) ) ) ;
2014-07-29 09:10:25 -04:00
theResourceDef . addSearchParam ( param ) ;
}
2014-05-05 05:57:43 -07:00
}
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 ;
}
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-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 > > ( ) ;
2015-05-31 16:52:31 -04:00
2015-02-14 13:27:11 -05:00
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-31 16:52:31 -04:00
2015-05-02 14:04:43 -07:00
@SuppressWarnings ( " unchecked " )
Class < ? extends IBase > dtType = ( Class < ? extends IBase > ) Class . forName ( nextValue ) ;
retVal . add ( dtType ) ;
2015-05-31 16:52:31 -04:00
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 ) ;
2015-02-14 13:27:11 -05:00
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 ) ;
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 ) ) {
2015-05-05 18:16:22 -04:00
throw new ConfigurationException ( " Class is not assignable from " + IBaseResource . class . getSimpleName ( ) + " : " + nextValue ) ;
2015-02-14 13:27:11 -05:00
}
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 ) ;
2015-02-14 13:27:11 -05:00
}
} else {
2015-05-05 18:16:22 -04:00
throw new ConfigurationException ( " Unexpected property in version property file: " + nextKey + " = " + nextValue ) ;
2015-02-14 13:27:11 -05:00
}
}
} catch ( IOException e ) {
throw new ConfigurationException ( " Failed to load model property file from classpath: " + " /ca/uhn/fhir/model/dstu/model.properties " ) ;
}
2015-05-31 16:52:31 -04:00
return retVal ;
2015-04-20 07:31:45 -04:00
}
2014-02-17 17:58:32 -05:00
}