2014-02-17 17:58:32 -05:00
package ca.uhn.fhir.context ;
2014-02-22 15:33:02 -05:00
import static org.apache.commons.lang3.StringUtils.* ;
2014-02-17 17:58:32 -05:00
import java.lang.reflect.Field ;
2014-02-19 13:02:51 -05:00
import java.lang.reflect.ParameterizedType ;
2014-02-17 17:58:32 -05:00
import java.util.ArrayList ;
2014-02-18 08:26:49 -05:00
import java.util.Arrays ;
2014-02-20 12:13:05 -05:00
import java.util.Collection ;
2014-02-17 17:58:32 -05:00
import java.util.HashMap ;
import java.util.HashSet ;
2014-02-18 18:10:50 -05:00
import java.util.Iterator ;
2014-02-19 11:59:12 -05:00
import java.util.LinkedList ;
2014-02-17 17:58:32 -05:00
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
2014-02-19 11:59:12 -05:00
import java.util.TreeMap ;
2014-02-23 18:13:59 -05:00
import java.util.TreeSet ;
2014-02-17 17:58:32 -05:00
import ca.uhn.fhir.model.api.CodeableConceptElement ;
import ca.uhn.fhir.model.api.ICodeEnum ;
import ca.uhn.fhir.model.api.ICompositeDatatype ;
import ca.uhn.fhir.model.api.ICompositeElement ;
import ca.uhn.fhir.model.api.IDatatype ;
import ca.uhn.fhir.model.api.IElement ;
import ca.uhn.fhir.model.api.IPrimitiveDatatype ;
import ca.uhn.fhir.model.api.IResource ;
2014-02-20 12:13:05 -05:00
import ca.uhn.fhir.model.api.IResourceBlock ;
2014-02-17 17:58:32 -05:00
import ca.uhn.fhir.model.api.ResourceReference ;
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 ;
import ca.uhn.fhir.model.api.annotation.ChildResource ;
import ca.uhn.fhir.model.api.annotation.Choice ;
2014-02-18 18:10:50 -05:00
import ca.uhn.fhir.model.api.annotation.CodeTableDef ;
2014-02-19 11:59:12 -05:00
import ca.uhn.fhir.model.api.annotation.DatatypeDef ;
2014-02-23 18:13:59 -05:00
import ca.uhn.fhir.model.api.annotation.Extension ;
2014-02-19 11:59:12 -05:00
import ca.uhn.fhir.model.api.annotation.Narrative ;
2014-02-18 18:10:50 -05:00
import ca.uhn.fhir.model.api.annotation.ResourceDef ;
2014-02-22 15:33:02 -05:00
import ca.uhn.fhir.model.datatype.CodeDt ;
import ca.uhn.fhir.model.datatype.DateDt ;
2014-02-18 18:10:50 -05:00
import ca.uhn.fhir.model.datatype.ICodedDatatype ;
2014-02-19 11:59:12 -05:00
import ca.uhn.fhir.model.datatype.NarrativeDt ;
2014-02-21 21:06:11 -05:00
import ca.uhn.fhir.model.datatype.XhtmlDt ;
2014-02-17 17:58:32 -05:00
class ModelScanner {
private static final org . slf4j . Logger ourLog = org . slf4j . LoggerFactory . getLogger ( ModelScanner . class ) ;
2014-02-18 18:10:50 -05:00
private Map < Class < ? extends IElement > , BaseRuntimeElementDefinition < ? > > myClassToElementDefinitions = new HashMap < Class < ? extends IElement > , BaseRuntimeElementDefinition < ? > > ( ) ;
private Map < String , RuntimeResourceDefinition > myNameToResourceDefinitions = new HashMap < String , RuntimeResourceDefinition > ( ) ;
2014-02-18 08:26:49 -05:00
private Set < Class < ? extends IElement > > myScanAlso = new HashSet < Class < ? extends IElement > > ( ) ;
2014-02-18 18:10:50 -05:00
2014-02-17 17:58:32 -05:00
// private Map<String, RuntimeResourceDefinition>
// myNameToDatatypeDefinitions = new HashMap<String,
// RuntimeDatatypeDefinition>();
2014-02-22 15:33:02 -05:00
private Set < Class < ? extends ICodeEnum > > myScanAlsoCodeTable = new HashSet < Class < ? extends ICodeEnum > > ( ) ;
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition ;
2014-02-18 08:26:49 -05:00
2014-02-17 17:58:32 -05:00
ModelScanner ( Class < ? extends IResource > . . . theResourceTypes ) throws ConfigurationException {
2014-02-18 18:10:50 -05:00
Set < Class < ? extends IElement > > toScan = new HashSet < Class < ? extends IElement > > ( Arrays . asList ( theResourceTypes ) ) ;
2014-02-19 11:59:12 -05:00
toScan . add ( NarrativeDt . class ) ;
2014-02-22 15:33:02 -05:00
toScan . add ( DateDt . class ) ;
toScan . add ( CodeDt . class ) ;
2014-02-19 11:59:12 -05:00
2014-02-18 18:10:50 -05:00
do {
for ( Class < ? extends IElement > nextClass : toScan ) {
scan ( nextClass ) ;
}
2014-02-19 11:59:12 -05:00
for ( Iterator < Class < ? extends IElement > > iter = myScanAlso . iterator ( ) ; iter . hasNext ( ) ; ) {
2014-02-18 18:10:50 -05:00
if ( myClassToElementDefinitions . containsKey ( iter . next ( ) ) ) {
iter . remove ( ) ;
}
}
toScan . clear ( ) ;
toScan . addAll ( myScanAlso ) ;
myScanAlso . clear ( ) ;
2014-02-19 13:02:51 -05:00
} while ( ! toScan . isEmpty ( ) ) ;
2014-02-19 11:59:12 -05:00
2014-02-18 18:10:50 -05:00
for ( BaseRuntimeElementDefinition < ? > next : myClassToElementDefinitions . values ( ) ) {
next . sealAndInitialize ( myClassToElementDefinitions ) ;
2014-02-17 17:58:32 -05:00
}
2014-02-19 11:59:12 -05:00
2014-02-23 18:13:59 -05:00
myRuntimeChildUndeclaredExtensionDefinition = new RuntimeChildUndeclaredExtensionDefinition ( ) ;
myRuntimeChildUndeclaredExtensionDefinition . sealAndInitialize ( myClassToElementDefinitions ) ;
2014-02-22 15:33:02 -05:00
2014-02-19 11:59:12 -05:00
ourLog . info ( " Done scanning FHIR library, found {} model entries " , myClassToElementDefinitions . size ( ) ) ;
2014-02-17 17:58:32 -05:00
}
2014-02-22 15:33:02 -05:00
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition ( ) {
return myRuntimeChildUndeclaredExtensionDefinition ;
}
public Map < Class < ? extends IElement > , BaseRuntimeElementDefinition < ? > > getClassToElementDefinitions ( ) {
return myClassToElementDefinitions ;
}
public Map < String , RuntimeResourceDefinition > getNameToResourceDefinitions ( ) {
return ( myNameToResourceDefinitions ) ;
}
private void addScanAlso ( Class < ? extends IElement > theType ) {
if ( theType . isInterface ( ) ) {
return ;
}
myScanAlso . add ( theType ) ;
}
2014-02-20 12:13:05 -05:00
private void scan ( Class < ? extends IElement > theClass ) throws ConfigurationException {
2014-02-18 18:10:50 -05:00
BaseRuntimeElementDefinition < ? > existingDef = myClassToElementDefinitions . get ( theClass ) ;
2014-02-17 17:58:32 -05:00
if ( existingDef ! = null ) {
2014-02-20 12:13:05 -05:00
return ;
2014-02-17 17:58:32 -05:00
}
2014-02-18 18:10:50 -05:00
ResourceDef resourceDefinition = theClass . getAnnotation ( ResourceDef . class ) ;
2014-02-17 17:58:32 -05:00
if ( resourceDefinition ! = null ) {
if ( ! IResource . class . isAssignableFrom ( theClass ) ) {
2014-02-21 21:06:11 -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 " )
Class < ? extends IResource > resClass = ( Class < ? extends IResource > ) theClass ;
2014-02-20 12:13:05 -05:00
scanResource ( resClass , resourceDefinition ) ;
2014-02-17 17:58:32 -05:00
}
2014-02-18 18:10:50 -05:00
DatatypeDef datatypeDefinition = theClass . getAnnotation ( DatatypeDef . class ) ;
2014-02-17 17:58:32 -05:00
if ( datatypeDefinition ! = null ) {
if ( ICompositeDatatype . class . isAssignableFrom ( theClass ) ) {
@SuppressWarnings ( " unchecked " )
Class < ? extends ICompositeDatatype > resClass = ( Class < ? extends ICompositeDatatype > ) theClass ;
2014-02-20 12:13:05 -05:00
scanCompositeDatatype ( resClass , datatypeDefinition ) ;
2014-02-17 17:58:32 -05:00
} else if ( IPrimitiveDatatype . class . isAssignableFrom ( theClass ) ) {
2014-02-20 12:13:05 -05:00
@SuppressWarnings ( { " unchecked " } )
Class < ? extends IPrimitiveDatatype < ? > > resClass = ( Class < ? extends IPrimitiveDatatype < ? > > ) theClass ;
scanPrimitiveDatatype ( resClass , datatypeDefinition ) ;
2014-02-17 17:58:32 -05:00
} else {
2014-02-21 21:06:11 -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
}
}
2014-02-18 18:10:50 -05:00
CodeTableDef codeTableDefinition = theClass . getAnnotation ( CodeTableDef . class ) ;
if ( codeTableDefinition ! = null ) {
if ( ICodeEnum . class . isAssignableFrom ( theClass ) ) {
@SuppressWarnings ( " unchecked " )
Class < ? extends ICodeEnum > resClass = ( Class < ? extends ICodeEnum > ) theClass ;
2014-02-20 12:13:05 -05:00
scanCodeTable ( resClass , codeTableDefinition ) ;
2014-02-18 18:10:50 -05:00
} else {
2014-02-21 21:06:11 -05:00
throw new ConfigurationException ( " Type contains a @ " + CodeTableDef . class . getSimpleName ( ) + " annotation but does not implement " + ICodeEnum . class . getCanonicalName ( ) + " : " + theClass . getCanonicalName ( ) ) ;
2014-02-18 18:10:50 -05:00
}
}
2014-02-21 21:06:11 -05:00
2014-02-20 12:13:05 -05:00
Block blockDefinition = theClass . getAnnotation ( Block . class ) ;
if ( blockDefinition ! = null ) {
if ( IResourceBlock . class . isAssignableFrom ( theClass ) ) {
@SuppressWarnings ( " unchecked " )
Class < ? extends IResourceBlock > blockClass = ( Class < ? extends IResourceBlock > ) theClass ;
scanBlock ( blockClass , blockDefinition ) ;
2014-02-21 21:06:11 -05:00
} else {
throw new ConfigurationException ( " Type contains a @ " + Block . class . getSimpleName ( ) + " annotation but does not implement " + IResourceBlock . class . getCanonicalName ( ) + " : " + theClass . getCanonicalName ( ) ) ;
2014-02-20 12:13:05 -05:00
}
}
if ( blockDefinition = = null & & codeTableDefinition = = null & & datatypeDefinition = = null & & resourceDefinition = = null ) {
2014-02-21 21:06:11 -05:00
throw new ConfigurationException ( " Resource type does not contain any valid HAPI-FHIR annotations: " + theClass . getCanonicalName ( ) ) ;
2014-02-20 12:13:05 -05:00
}
}
2014-02-23 18:13:59 -05:00
2014-02-20 12:13:05 -05:00
private void scanBlock ( Class < ? extends IResourceBlock > theClass , Block theBlockDefinition ) {
ourLog . debug ( " Scanning resource block class: {} " , theClass . getName ( ) ) ;
2014-02-18 18:10:50 -05:00
2014-02-20 12:13:05 -05:00
String resourceName = theBlockDefinition . name ( ) ;
if ( isBlank ( resourceName ) ) {
throw new ConfigurationException ( " Block type @ " + Block . class . getSimpleName ( ) + " annotation contains no name: " + theClass . getCanonicalName ( ) ) ;
}
RuntimeResourceBlockDefinition resourceDef = new RuntimeResourceBlockDefinition ( resourceName , theClass ) ;
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
scanCompositeElementForChildren ( theClass , resourceDef , null ) ;
2014-02-17 17:58:32 -05:00
}
2014-02-22 15:33:02 -05:00
private String scanCodeTable ( Class < ? extends ICodeEnum > theCodeType , CodeTableDef theCodeTableDefinition ) {
return null ; // TODO: implement
}
2014-02-20 12:13:05 -05:00
private void scanCompositeDatatype ( Class < ? extends ICompositeDatatype > theClass , DatatypeDef theDatatypeDefinition ) {
2014-02-17 17:58:32 -05:00
ourLog . debug ( " Scanning resource class: {} " , theClass . getName ( ) ) ;
String resourceName = theDatatypeDefinition . name ( ) ;
if ( isBlank ( resourceName ) ) {
2014-02-18 18:10:50 -05:00
throw new ConfigurationException ( " Resource type @ " + ResourceDef . class . getSimpleName ( ) + " annotation contains no resource name: " + theClass . getCanonicalName ( ) ) ;
2014-02-17 17:58:32 -05:00
}
RuntimeCompositeDatatypeDefinition resourceDef = new RuntimeCompositeDatatypeDefinition ( resourceName , theClass ) ;
2014-02-18 18:10:50 -05:00
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
2014-02-19 13:02:51 -05:00
scanCompositeElementForChildren ( theClass , resourceDef , null ) ;
2014-02-17 17:58:32 -05:00
}
2014-02-19 11:59:12 -05:00
@SuppressWarnings ( " unchecked " )
2014-02-19 13:02:51 -05:00
private void scanCompositeElementForChildren ( Class < ? extends ICompositeElement > theClass , BaseRuntimeElementCompositeDefinition < ? > theDefinition , Integer theIdentifierOrder ) {
2014-02-17 17:58:32 -05:00
Set < String > elementNames = new HashSet < String > ( ) ;
2014-02-23 18:13:59 -05:00
TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > orderToElementDef = new TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > ( ) ;
TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > orderToExtensionDef = new TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > ( ) ;
2014-02-19 11:59:12 -05:00
LinkedList < Class < ? extends ICompositeElement > > classes = new LinkedList < Class < ? extends ICompositeElement > > ( ) ;
Class < ? extends ICompositeElement > current = theClass ;
do {
classes . push ( current ) ;
if ( ICompositeElement . class . isAssignableFrom ( current . getSuperclass ( ) ) ) {
current = ( Class < ? extends ICompositeElement > ) current . getSuperclass ( ) ;
2014-02-19 17:33:46 -05:00
} else {
2014-02-19 11:59:12 -05:00
current = null ;
}
2014-02-19 17:33:46 -05:00
} while ( current ! = null ) ;
2014-02-19 11:59:12 -05:00
for ( Class < ? extends ICompositeElement > next : classes ) {
2014-02-23 18:13:59 -05:00
scanCompositeElementForChildren ( next , theDefinition , elementNames , orderToElementDef , orderToExtensionDef ) ;
2014-02-19 11:59:12 -05:00
}
2014-02-20 12:13:05 -05:00
while ( orderToElementDef . size ( ) > 0 & & orderToElementDef . firstKey ( ) < 0 ) {
2014-02-23 18:13:59 -05:00
BaseRuntimeDeclaredChildDefinition elementDef = orderToElementDef . remove ( orderToElementDef . firstKey ( ) ) ;
2014-02-19 13:02:51 -05:00
if ( elementDef . getElementName ( ) . equals ( " identifier " ) ) {
orderToElementDef . put ( theIdentifierOrder , elementDef ) ;
2014-02-19 17:33:46 -05:00
} else {
2014-02-19 13:02:51 -05:00
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 ( ) ) ;
for ( Integer i : orders ) {
BaseRuntimeChildDefinition nextChild = orderToElementDef . get ( i ) ;
if ( nextChild ! = null ) {
theDefinition . addChild ( nextChild ) ;
}
BaseRuntimeDeclaredChildDefinition nextExt = orderToExtensionDef . get ( i ) ;
if ( nextExt ! = null ) {
theDefinition . addExtension ( ( RuntimeChildDeclaredExtensionDefinition ) nextExt ) ;
2014-02-19 11:59:12 -05:00
}
}
}
2014-02-19 13:02:51 -05:00
@SuppressWarnings ( " unchecked " )
2014-02-23 18:13:59 -05:00
private void scanCompositeElementForChildren ( Class < ? extends ICompositeElement > theClass , BaseRuntimeElementCompositeDefinition < ? > theDefinition , Set < String > elementNames , TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > theOrderToElementDef , TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > theOrderToExtensionDef ) {
2014-02-19 11:59:12 -05:00
for ( Field next : theClass . getDeclaredFields ( ) ) {
2014-02-19 17:33:46 -05:00
2014-02-19 11:59:12 -05:00
Narrative hasNarrative = next . getAnnotation ( Narrative . class ) ;
if ( hasNarrative ! = null ) {
RuntimeChildNarrativeDefinition def ;
try {
def = new RuntimeChildNarrativeDefinition ( next , hasNarrative . name ( ) ) ;
} catch ( Exception e ) {
throw new ConfigurationException ( " Failed to find narrative field " , e ) ;
}
theDefinition . addChild ( def ) ;
2014-02-19 17:33:46 -05:00
continue ;
2014-02-19 11:59:12 -05:00
}
2014-02-18 08:26:49 -05:00
Child element = next . getAnnotation ( Child . class ) ;
2014-02-17 17:58:32 -05:00
if ( element = = null ) {
2014-02-19 17:33:46 -05:00
ourLog . debug ( " Ignoring non-type field ' " + next . getName ( ) + " ' on target type: " + theClass ) ;
2014-02-17 17:58:32 -05:00
continue ;
}
String elementName = element . name ( ) ;
int order = element . order ( ) ;
int min = element . min ( ) ;
int max = element . max ( ) ;
2014-02-23 18:13:59 -05:00
TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > orderMap = theOrderToElementDef ;
Extension extensionAttr = next . getAnnotation ( Extension . class ) ;
if ( extensionAttr ! = null ) {
orderMap = theOrderToExtensionDef ;
}
2014-02-20 12:13:05 -05:00
/ *
2014-02-21 21:06:11 -05:00
* Anything that ' s marked as unknown is given a new ID that is < 0 so
* that it doesn ' t conflict wityh any given IDs and can be figured
* out later
2014-02-20 12:13:05 -05:00
* /
2014-02-23 18:13:59 -05:00
while ( order = = Child . ORDER_UNKNOWN & & orderMap . containsKey ( order ) ) {
2014-02-19 11:59:12 -05:00
order - - ;
}
2014-02-19 17:33:46 -05:00
2014-02-18 08:26:49 -05:00
Choice choiceAttr = element . choice ( ) ;
2014-02-17 17:58:32 -05:00
List < Class < ? extends IElement > > choiceTypes = new ArrayList < Class < ? extends IElement > > ( ) ;
for ( Class < ? extends IElement > nextChoiceType : choiceAttr . types ( ) ) {
choiceTypes . add ( nextChoiceType ) ;
}
2014-02-23 18:13:59 -05:00
if ( orderMap . containsKey ( order ) ) {
throw new ConfigurationException ( " Detected duplicate field order ' " + order + " ' for element named ' " + elementName + " ' in type ' " + theClass . getCanonicalName ( ) + " ' " ) ;
2014-02-17 17:58:32 -05:00
}
if ( elementNames . contains ( elementName ) ) {
throw new ConfigurationException ( " Detected duplicate field name ' " + elementName + " ' in type ' " + theClass . getCanonicalName ( ) + " ' " ) ;
}
2014-02-23 18:13:59 -05:00
Class < ? > nextElementType = determineElementType ( next ) ;
2014-02-21 21:06:11 -05:00
2014-02-18 08:26:49 -05:00
ChildResource resRefAnnotation = next . getAnnotation ( ChildResource . class ) ;
2014-02-17 17:58:32 -05:00
if ( choiceTypes . isEmpty ( ) = = false ) {
2014-02-18 08:26:49 -05:00
/ *
* Child is a choice element
* /
2014-02-21 21:06:11 -05:00
for ( Class < ? extends IElement > nextType : choiceTypes ) {
addScanAlso ( nextType ) ;
}
2014-02-18 08:26:49 -05:00
RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition ( next , elementName , min , max , choiceTypes ) ;
2014-02-23 18:13:59 -05:00
orderMap . put ( order , def ) ;
2014-02-17 17:58:32 -05:00
2014-02-23 18:13:59 -05:00
} else if ( extensionAttr ! = null ) {
/ *
* Child is an extension
* /
Class < ? extends IElement > et = ( Class < ? extends IElement > ) nextElementType ;
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition ( next , min , max , elementName , extensionAttr . url ( ) , et ) ;
orderMap . put ( order , def ) ;
if ( IElement . class . isAssignableFrom ( nextElementType ) ) {
addScanAlso ( ( Class < ? extends IElement > ) nextElementType ) ;
}
2014-02-20 12:13:05 -05:00
} else if ( ResourceReference . class . isAssignableFrom ( nextElementType ) ) {
2014-02-18 08:26:49 -05:00
/ *
* Child is a resource reference
* /
2014-02-17 17:58:32 -05:00
if ( resRefAnnotation = = null ) {
2014-02-21 21:06:11 -05:00
throw new ConfigurationException ( " Field ' " + elementName + " ' in type ' " + theClass . getCanonicalName ( ) + " ' is a resource reference but does not have a @ " + ChildResource . class . getSimpleName ( ) + " annotation " ) ;
2014-02-17 17:58:32 -05:00
}
2014-02-18 08:26:49 -05:00
Class < ? extends IResource > [ ] refType = resRefAnnotation . types ( ) ;
List < Class < ? extends IResource > > refTypesList = Arrays . asList ( refType ) ;
2014-02-21 21:06:11 -05:00
for ( Class < ? extends IElement > nextType : refTypesList ) {
addScanAlso ( nextType ) ;
}
2014-02-18 08:26:49 -05:00
RuntimeChildResourceDefinition def = new RuntimeChildResourceDefinition ( next , elementName , min , max , refTypesList ) ;
2014-02-23 18:13:59 -05:00
orderMap . put ( order , def ) ;
2014-02-18 18:10:50 -05:00
2014-02-20 12:13:05 -05:00
} else if ( IResourceBlock . class . isAssignableFrom ( nextElementType ) ) {
/ *
* 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-21 21:06:11 -05:00
Class < ? extends IResourceBlock > blockDef = ( Class < ? extends IResourceBlock > ) nextElementType ;
addScanAlso ( blockDef ) ;
2014-02-20 12:13:05 -05:00
RuntimeChildResourceBlockDefinition def = new RuntimeChildResourceBlockDefinition ( next , min , max , elementName , blockDef ) ;
2014-02-23 18:13:59 -05:00
orderMap . put ( order , def ) ;
2014-02-21 21:06:11 -05:00
2014-02-20 12:13:05 -05:00
} else if ( IDatatype . class . isAssignableFrom ( nextElementType ) ) {
Class < ? extends IDatatype > nextDatatype = ( Class < ? extends IDatatype > ) nextElementType ;
2014-02-17 17:58:32 -05:00
2014-02-21 21:06:11 -05:00
addScanAlso ( nextDatatype ) ;
2014-02-18 18:10:50 -05:00
BaseRuntimeChildDatatypeDefinition def ;
2014-02-20 12:13:05 -05:00
if ( IPrimitiveDatatype . class . isAssignableFrom ( nextElementType ) ) {
2014-02-18 18:10:50 -05:00
def = new RuntimeChildPrimitiveDatatypeDefinition ( next , elementName , min , max , nextDatatype ) ;
} else {
def = new RuntimeChildCompositeDatatypeDefinition ( next , elementName , min , max , nextDatatype ) ;
}
CodeableConceptElement concept = next . getAnnotation ( CodeableConceptElement . class ) ;
if ( concept ! = null ) {
if ( ! ICodedDatatype . class . isAssignableFrom ( nextDatatype ) ) {
2014-02-21 21:06:11 -05: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
// TODO: handle codes
2014-02-20 12:13:05 -05:00
} else {
throw new ConfigurationException ( " Field ' " + elementName + " ' in type ' " + theClass . getCanonicalName ( ) + " ' is not a valid child type " ) ;
2014-02-17 17:58:32 -05:00
}
elementNames . add ( elementName ) ;
}
}
2014-02-23 18:13:59 -05:00
private Class < ? > determineElementType ( Field next ) {
Class < ? > nextElementType = next . getType ( ) ;
if ( List . class . equals ( nextElementType ) ) {
nextElementType = getGenericCollectionTypeOfField ( next ) ;
} else if ( Collection . class . isAssignableFrom ( nextElementType ) ) {
throw new ConfigurationException ( " Field ' " + next . getName ( ) + " ' in type ' " + next . getClass ( ) . getCanonicalName ( ) + " ' is a Collection - Only java.util.List curently supported " ) ;
}
return nextElementType ;
}
2014-02-22 15:33:02 -05:00
private String scanPrimitiveDatatype ( Class < ? extends IPrimitiveDatatype < ? > > theClass , DatatypeDef theDatatypeDefinition ) {
ourLog . debug ( " Scanning resource class: {} " , theClass . getName ( ) ) ;
String resourceName = theDatatypeDefinition . name ( ) ;
if ( isBlank ( resourceName ) ) {
throw new ConfigurationException ( " Resource type @ " + ResourceDef . class . getSimpleName ( ) + " annotation contains no resource name: " + theClass . getCanonicalName ( ) ) ;
2014-02-21 21:06:11 -05:00
}
2014-02-22 15:33:02 -05:00
BaseRuntimeElementDefinition < ? > resourceDef ;
if ( theClass . equals ( XhtmlDt . class ) ) {
@SuppressWarnings ( " unchecked " )
Class < XhtmlDt > clazz = ( Class < XhtmlDt > ) theClass ;
resourceDef = new RuntimePrimitiveDatatypeNarrativeDefinition ( resourceName , clazz ) ;
} else {
resourceDef = new RuntimePrimitiveDatatypeDefinition ( resourceName , theClass ) ;
}
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
return resourceName ;
}
private String scanResource ( Class < ? extends IResource > theClass , ResourceDef resourceDefinition ) {
ourLog . debug ( " Scanning resource class: {} " , theClass . getName ( ) ) ;
String resourceName = resourceDefinition . name ( ) ;
if ( isBlank ( resourceName ) ) {
throw new ConfigurationException ( " Resource type @ " + ResourceDef . class . getSimpleName ( ) + " annotation contains no resource name: " + theClass . getCanonicalName ( ) ) ;
}
if ( myNameToResourceDefinitions . containsKey ( resourceName ) ) {
if ( ! myNameToResourceDefinitions . get ( resourceName ) . getImplementingClass ( ) . equals ( theClass ) ) {
throw new ConfigurationException ( " Detected duplicate element name ' " + resourceName + " ' in types ' " + theClass . getCanonicalName ( ) + " ' and ' " + myNameToResourceDefinitions . get ( resourceName ) . getImplementingClass ( ) + " ' " ) ;
}
return resourceName ;
}
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition ( theClass , resourceName ) ;
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
myNameToResourceDefinitions . put ( resourceName , resourceDef ) ;
scanCompositeElementForChildren ( theClass , resourceDef , resourceDefinition . identifierOrder ( ) ) ;
return resourceName ;
2014-02-21 21:06:11 -05:00
}
2014-02-19 17:33:46 -05:00
private static Class < ? > getGenericCollectionTypeOfField ( Field next ) {
2014-02-21 21:06:11 -05:00
// Type genericSuperclass = next.getType().getGenericSuperclass();
2014-02-19 17:33:46 -05:00
Class < ? > type ;
2014-02-21 21:06:11 -05:00
// if (genericSuperclass == null) {
ParameterizedType collectionType = ( ParameterizedType ) next . getGenericType ( ) ;
type = ( Class < ? > ) collectionType . getActualTypeArguments ( ) [ 0 ] ;
// }else {
// Type[] actualTypeArguments = ((ParameterizedType)
// genericSuperclass).getActualTypeArguments();
// type = (Class<?>) actualTypeArguments[0];
// }
2014-02-19 17:33:46 -05:00
return type ;
}
2014-02-17 17:58:32 -05:00
}