2014-02-17 17:58:32 -05:00
package ca.uhn.fhir.context ;
2014-04-24 12:25:47 -04:00
/ *
* # % L
* HAPI FHIR Library
* % %
* Copyright ( C ) 2014 University Health Network
* % %
* Licensed under the Apache License , Version 2 . 0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* # L %
* /
2014-07-04 09:27:36 -04:00
import static org.apache.commons.lang3.StringUtils.* ;
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-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-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-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-03-16 15:09:30 -04:00
import ca.uhn.fhir.model.api.BaseResourceReference ;
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-02-17 17:58:32 -05:00
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 ;
2014-07-04 09:27:36 -04:00
import ca.uhn.fhir.model.api.IExtension ;
2014-02-17 17:58:32 -05:00
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-18 18:10:50 -05:00
import ca.uhn.fhir.model.api.annotation.CodeTableDef ;
2014-02-19 11:59:12 -05:00
import ca.uhn.fhir.model.api.annotation.DatatypeDef ;
2014-03-10 12:43:49 -04:00
import ca.uhn.fhir.model.api.annotation.Description ;
2014-02-23 18:13:59 -05:00
import ca.uhn.fhir.model.api.annotation.Extension ;
2014-02-18 18:10:50 -05:00
import ca.uhn.fhir.model.api.annotation.ResourceDef ;
2014-05-05 05:57:43 -07:00
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition ;
2014-03-17 08:57:57 -04:00
import ca.uhn.fhir.model.dstu.composite.ContainedDt ;
2014-03-24 08:32:28 -04:00
import ca.uhn.fhir.model.dstu.composite.NarrativeDt ;
2014-03-10 12:43:49 -04:00
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt ;
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-03-10 18:17:30 -04:00
import ca.uhn.fhir.model.primitive.BoundCodeableConceptDt ;
2014-02-24 11:46:08 -05:00
import ca.uhn.fhir.model.primitive.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-02-18 18:10:50 -05:00
private Map < Class < ? extends IElement > , BaseRuntimeElementDefinition < ? > > myClassToElementDefinitions = new HashMap < Class < ? extends IElement > , BaseRuntimeElementDefinition < ? > > ( ) ;
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 > ( ) ;
2014-02-17 17:58:32 -05:00
// private Map<String, RuntimeResourceDefinition>
// myNameToDatatypeDefinitions = new HashMap<String,
// RuntimeDatatypeDefinition>();
2014-02-22 15:33:02 -05:00
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition ;
2014-02-18 08:26:49 -05:00
2014-03-11 08:39:55 -04:00
private Set < Class < ? extends IElement > > myScanAlso = new HashSet < Class < ? extends IElement > > ( ) ;
private Set < Class < ? extends ICodeEnum > > myScanAlsoCodeTable = new HashSet < Class < ? extends ICodeEnum > > ( ) ;
2014-03-04 16:41:18 -05:00
ModelScanner ( Class < ? extends IResource > theResourceTypes ) throws ConfigurationException {
2014-03-05 17:35:06 -05:00
Set < Class < ? extends IElement > > singleton = new HashSet < Class < ? extends IElement > > ( ) ;
2014-03-04 16:41:18 -05:00
singleton . add ( theResourceTypes ) ;
2014-07-04 09:27:36 -04:00
init ( null , singleton ) ;
2014-03-04 16:41:18 -05:00
}
2014-03-04 08:13:28 -05:00
ModelScanner ( Collection < Class < ? extends IResource > > theResourceTypes ) throws ConfigurationException {
2014-07-02 08:57:07 -04:00
init ( null , new HashSet < Class < ? extends IElement > > ( theResourceTypes ) ) ;
}
ModelScanner ( Map < Class < ? extends IElement > , BaseRuntimeElementDefinition < ? > > theExistingDefinitions , Collection < Class < ? extends IResource > > theResourceTypes ) throws ConfigurationException {
init ( theExistingDefinitions , new HashSet < Class < ? extends IElement > > ( theResourceTypes ) ) ;
2014-03-04 16:41:18 -05:00
}
2014-02-18 18:10:50 -05:00
2014-03-11 08:39:55 -04:00
public Map < Class < ? extends IElement > , BaseRuntimeElementDefinition < ? > > getClassToElementDefinitions ( ) {
return myClassToElementDefinitions ;
}
public Map < String , RuntimeResourceDefinition > getNameToResourceDefinitions ( ) {
return ( myNameToResourceDefinitions ) ;
}
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition ( ) {
return myRuntimeChildUndeclaredExtensionDefinition ;
}
private void addScanAlso ( Class < ? extends IElement > theType ) {
if ( theType . isInterface ( ) ) {
return ;
}
myScanAlso . add ( theType ) ;
}
private Class < ? > determineElementType ( Field next ) {
Class < ? > nextElementType = next . getType ( ) ;
if ( List . class . equals ( nextElementType ) ) {
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 ) ;
}
}
2014-07-02 08:57:07 -04:00
private void init ( Map < Class < ? extends IElement > , BaseRuntimeElementDefinition < ? > > theExistingDefinitions , Set < Class < ? extends IElement > > toScan ) {
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 ( ) ;
InputStream str = ModelScanner . class . getResourceAsStream ( " /ca/uhn/fhir/model/dstu/model.properties " ) ;
if ( str = = null ) {
str = ModelScanner . class . getResourceAsStream ( " ca/uhn/fhir/model/dstu/model.properties " ) ;
}
if ( str = = null ) {
throw new ConfigurationException ( " Can not find model property file on classpath: " + " /ca/uhn/fhir/model/dstu/model.properties " ) ;
}
Properties prop = new Properties ( ) ;
try {
prop . load ( str ) ;
for ( Object nextValue : prop . values ( ) ) {
try {
@SuppressWarnings ( " unchecked " )
Class < ? extends IElement > nextClass = ( Class < ? extends IElement > ) Class . forName ( ( String ) nextValue ) ;
if ( ! IElement . class . isAssignableFrom ( nextClass ) ) {
2014-05-28 16:07:53 -04:00
ourLog . warn ( " Class is not assignable from " + IElement . class . getSimpleName ( ) + " : " + nextValue ) ;
2014-05-07 08:18:43 -04:00
continue ;
}
2014-05-28 16:07:53 -04:00
2014-05-07 08:18:43 -04:00
toScan . add ( nextClass ) ;
} catch ( ClassNotFoundException e ) {
ourLog . warn ( " Unknown class exception: " + nextValue , e ) ;
}
}
} catch ( IOException e ) {
throw new ConfigurationException ( " Failed to load model property file from classpath: " + " /ca/uhn/fhir/model/dstu/model.properties " ) ;
}
// 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 {
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-07-02 08:57:07 -04:00
for ( Entry < Class < ? extends IElement > , 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 ( ) ;
2014-02-18 18:10:50 -05:00
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-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 ;
2014-07-02 08:57:07 -04:00
ourLog . info ( " Done scanning FHIR library, found {} model entries in {}ms " , size , time ) ;
2014-02-17 17:58:32 -05:00
}
2014-02-20 12:13:05 -05:00
private void scan ( Class < ? extends IElement > theClass ) throws ConfigurationException {
2014-02-18 18:10:50 -05:00
BaseRuntimeElementDefinition < ? > existingDef = myClassToElementDefinitions . get ( theClass ) ;
2014-02-17 17:58:32 -05:00
if ( existingDef ! = null ) {
2014-02-20 12:13:05 -05:00
return ;
2014-02-17 17:58:32 -05:00
}
2014-02-18 18:10:50 -05:00
ResourceDef resourceDefinition = theClass . getAnnotation ( ResourceDef . class ) ;
2014-02-17 17:58:32 -05:00
if ( resourceDefinition ! = null ) {
if ( ! IResource . class . isAssignableFrom ( theClass ) ) {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException ( " Resource type contains a @ " + ResourceDef . class . getSimpleName ( ) + " annotation but does not implement " + IResource . class . getCanonicalName ( ) + " : " + theClass . getCanonicalName ( ) ) ;
2014-02-17 17:58:32 -05:00
}
@SuppressWarnings ( " unchecked " )
Class < ? extends IResource > resClass = ( Class < ? extends IResource > ) theClass ;
2014-02-20 12:13:05 -05:00
scanResource ( resClass , resourceDefinition ) ;
2014-02-17 17:58:32 -05:00
}
2014-02-18 18:10:50 -05:00
DatatypeDef datatypeDefinition = theClass . getAnnotation ( DatatypeDef . class ) ;
2014-02-17 17:58:32 -05:00
if ( datatypeDefinition ! = null ) {
if ( ICompositeDatatype . class . isAssignableFrom ( theClass ) ) {
@SuppressWarnings ( " unchecked " )
Class < ? extends ICompositeDatatype > resClass = ( Class < ? extends ICompositeDatatype > ) theClass ;
2014-02-20 12:13:05 -05:00
scanCompositeDatatype ( resClass , datatypeDefinition ) ;
2014-02-17 17:58:32 -05:00
} else if ( IPrimitiveDatatype . class . isAssignableFrom ( theClass ) ) {
2014-02-20 12:13:05 -05:00
@SuppressWarnings ( { " unchecked " } )
Class < ? extends IPrimitiveDatatype < ? > > resClass = ( Class < ? extends IPrimitiveDatatype < ? > > ) theClass ;
scanPrimitiveDatatype ( resClass , datatypeDefinition ) ;
2014-02-17 17:58:32 -05:00
} else {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException ( " Resource type contains a @ " + DatatypeDef . class . getSimpleName ( ) + " annotation but does not implement " + IDatatype . class . getCanonicalName ( ) + " : " + theClass . getCanonicalName ( ) ) ;
2014-02-17 17:58:32 -05:00
}
}
2014-02-18 18:10:50 -05:00
CodeTableDef codeTableDefinition = theClass . getAnnotation ( CodeTableDef . class ) ;
if ( codeTableDefinition ! = null ) {
if ( ICodeEnum . class . isAssignableFrom ( theClass ) ) {
@SuppressWarnings ( " unchecked " )
Class < ? extends ICodeEnum > resClass = ( Class < ? extends ICodeEnum > ) theClass ;
2014-02-20 12:13:05 -05:00
scanCodeTable ( resClass , codeTableDefinition ) ;
2014-02-18 18:10:50 -05:00
} else {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException ( " Type contains a @ " + CodeTableDef . class . getSimpleName ( ) + " annotation but does not implement " + ICodeEnum . class . getCanonicalName ( ) + " : " + theClass . getCanonicalName ( ) ) ;
2014-02-18 18:10:50 -05:00
}
}
2014-02-21 21:06:11 -05:00
2014-02-20 12:13:05 -05:00
Block blockDefinition = theClass . getAnnotation ( Block . class ) ;
if ( blockDefinition ! = null ) {
if ( IResourceBlock . class . isAssignableFrom ( theClass ) ) {
@SuppressWarnings ( " unchecked " )
Class < ? extends IResourceBlock > blockClass = ( Class < ? extends IResourceBlock > ) theClass ;
scanBlock ( blockClass , blockDefinition ) ;
2014-02-21 21:06:11 -05:00
} else {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException ( " Type contains a @ " + Block . class . getSimpleName ( ) + " annotation but does not implement " + IResourceBlock . class . getCanonicalName ( ) + " : " + theClass . getCanonicalName ( ) ) ;
2014-02-20 12:13:05 -05:00
}
}
if ( blockDefinition = = null & & codeTableDefinition = = null & & datatypeDefinition = = null & & resourceDefinition = = null ) {
2014-02-21 21:06:11 -05:00
throw new ConfigurationException ( " Resource type does not contain any valid HAPI-FHIR annotations: " + theClass . getCanonicalName ( ) ) ;
2014-02-20 12:13:05 -05:00
}
}
private void scanBlock ( Class < ? extends IResourceBlock > theClass , Block theBlockDefinition ) {
ourLog . debug ( " Scanning resource block class: {} " , theClass . getName ( ) ) ;
2014-02-18 18:10:50 -05:00
2014-04-14 16:26:00 -04:00
String resourceName = theBlockDefinition . name ( ) ; // TODO: remove name
resourceName = theClass . getCanonicalName ( ) ;
2014-02-20 12:13:05 -05:00
if ( isBlank ( resourceName ) ) {
throw new ConfigurationException ( " Block type @ " + Block . class . getSimpleName ( ) + " annotation contains no name: " + theClass . getCanonicalName ( ) ) ;
}
RuntimeResourceBlockDefinition resourceDef = new RuntimeResourceBlockDefinition ( resourceName , theClass ) ;
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
2014-03-24 08:32:28 -04:00
scanCompositeElementForChildren ( theClass , resourceDef ) ;
2014-02-17 17:58:32 -05:00
}
2014-07-04 09:27:36 -04: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 ( ) ) ;
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-03-24 08:32:28 -04:00
private void scanCompositeElementForChildren ( Class < ? extends ICompositeElement > theClass , BaseRuntimeElementCompositeDefinition < ? > theDefinition ) {
2014-02-17 17:58:32 -05:00
Set < String > elementNames = new HashSet < String > ( ) ;
2014-02-23 18:13:59 -05:00
TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > orderToElementDef = new TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > ( ) ;
TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > orderToExtensionDef = new TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > ( ) ;
2014-02-19 11:59:12 -05:00
LinkedList < Class < ? extends ICompositeElement > > classes = new LinkedList < Class < ? extends ICompositeElement > > ( ) ;
Class < ? extends ICompositeElement > current = theClass ;
do {
classes . push ( current ) ;
if ( ICompositeElement . class . isAssignableFrom ( current . getSuperclass ( ) ) ) {
current = ( Class < ? extends ICompositeElement > ) current . getSuperclass ( ) ;
2014-02-19 17:33:46 -05:00
} else {
2014-02-19 11:59:12 -05:00
current = null ;
}
2014-02-19 17:33:46 -05:00
} while ( current ! = null ) ;
2014-02-19 11:59:12 -05:00
for ( Class < ? extends ICompositeElement > next : classes ) {
2014-03-25 13:42:38 -04:00
scanCompositeElementForChildren ( next , elementNames , orderToElementDef , orderToExtensionDef ) ;
2014-02-19 11:59:12 -05:00
}
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 " )
2014-05-07 08:18:43 -04:00
private void scanCompositeElementForChildren ( Class < ? extends ICompositeElement > 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-03-10 12:43:49 -04:00
Child childAnnotation = next . getAnnotation ( Child . class ) ;
if ( childAnnotation = = null ) {
2014-02-19 17:33:46 -05:00
ourLog . debug ( " Ignoring non-type field ' " + next . getName ( ) + " ' on target type: " + theClass ) ;
2014-02-17 17:58:32 -05:00
continue ;
}
2014-03-11 08:39:55 -04:00
2014-03-10 12:43:49 -04:00
Description descriptionAnnotation = next . getAnnotation ( Description . class ) ;
2014-02-17 17:58:32 -05:00
2014-03-10 12:43:49 -04:00
String elementName = childAnnotation . name ( ) ;
2014-03-24 08:32:28 -04:00
int order = childAnnotation . order ( ) ;
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 ;
}
2014-03-10 12:43:49 -04:00
int min = childAnnotation . min ( ) ;
int max = childAnnotation . max ( ) ;
2014-02-23 18:13:59 -05:00
TreeMap < Integer , BaseRuntimeDeclaredChildDefinition > orderMap = theOrderToElementDef ;
2014-03-03 09:05:55 -05:00
2014-02-23 18:13:59 -05:00
Extension extensionAttr = next . getAnnotation ( Extension . class ) ;
if ( extensionAttr ! = null ) {
orderMap = theOrderToExtensionDef ;
}
2014-03-03 09:05:55 -05:00
2014-02-20 12:13:05 -05:00
/ *
2014-05-28 16:07:53 -04:00
* Anything that ' s marked as unknown is given a new ID that is < 0 so that it doesn ' t conflict wityh any
* given IDs and can be figured out later
2014-02-20 12:13:05 -05:00
* /
2014-02-23 18:13:59 -05:00
while ( order = = Child . ORDER_UNKNOWN & & orderMap . containsKey ( order ) ) {
2014-02-19 11:59:12 -05:00
order - - ;
}
2014-02-19 17:33:46 -05:00
2014-02-17 17:58:32 -05:00
List < Class < ? extends IElement > > choiceTypes = new ArrayList < Class < ? extends IElement > > ( ) ;
2014-03-10 12:43:49 -04:00
for ( Class < ? extends IElement > nextChoiceType : childAnnotation . type ( ) ) {
2014-02-17 17:58:32 -05:00
choiceTypes . add ( nextChoiceType ) ;
}
2014-02-23 18:13:59 -05:00
if ( orderMap . containsKey ( order ) ) {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException ( " Detected duplicate field order ' " + childAnnotation . order ( ) + " ' for element named ' " + elementName + " ' in type ' " + theClass . getCanonicalName ( ) + " ' " ) ;
2014-02-17 17:58:32 -05:00
}
if ( elementNames . contains ( elementName ) ) {
throw new ConfigurationException ( " Detected duplicate field name ' " + elementName + " ' in type ' " + theClass . getCanonicalName ( ) + " ' " ) ;
}
2014-02-23 18:13:59 -05:00
Class < ? > nextElementType = determineElementType ( next ) ;
2014-02-21 21:06:11 -05:00
2014-03-17 08:57:57 -04:00
if ( nextElementType . equals ( ContainedDt . class ) ) {
/ *
* Child is contained resources
* /
RuntimeChildContainedResources def = new RuntimeChildContainedResources ( next , childAnnotation , descriptionAnnotation , elementName ) ;
orderMap . put ( order , def ) ;
2014-05-07 08:18:43 -04:00
2014-03-17 08:57:57 -04:00
} else if ( choiceTypes . size ( ) > 1 & & ! ResourceReferenceDt . class . isAssignableFrom ( nextElementType ) ) {
2014-02-18 08:26:49 -05:00
/ *
* Child is a choice element
* /
2014-02-21 21:06:11 -05:00
for ( Class < ? extends IElement > nextType : choiceTypes ) {
addScanAlso ( nextType ) ;
}
2014-03-10 12:43:49 -04:00
RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition ( next , elementName , childAnnotation , descriptionAnnotation , choiceTypes ) ;
2014-02-23 18:13:59 -05:00
orderMap . put ( order , def ) ;
2014-02-17 17:58:32 -05:00
2014-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
* /
Class < ? extends IElement > et = ( Class < ? extends IElement > ) nextElementType ;
2014-03-17 08:57:57 -04:00
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition ( next , childAnnotation , descriptionAnnotation , extensionAttr , elementName , extensionAttr . url ( ) , et ) ;
2014-02-23 18:13:59 -05:00
orderMap . put ( order , def ) ;
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 ) ;
}
2014-03-10 18:17:30 -04:00
} else if ( BaseResourceReference . class . isAssignableFrom ( nextElementType ) ) {
2014-02-18 08:26:49 -05:00
/ *
* Child is a resource reference
* /
2014-03-10 12:43:49 -04:00
List < Class < ? extends IResource > > refTypesList = new ArrayList < Class < ? extends IResource > > ( ) ;
for ( Class < ? extends IElement > nextType : childAnnotation . type ( ) ) {
2014-03-11 08:39:55 -04:00
if ( IResource . class . isAssignableFrom ( nextType ) = = false ) {
2014-03-24 08:32:28 -04:00
throw new ConfigurationException ( " Field ' " + next . getName ( ) + " ' in class ' " + next . getDeclaringClass ( ) . getCanonicalName ( ) + " ' is of type " + ResourceReferenceDt . class + " but contains a non-resource type: " + nextType . getCanonicalName ( ) ) ;
2014-03-10 12:43:49 -04:00
}
refTypesList . add ( ( Class < ? extends IResource > ) nextType ) ;
2014-02-21 21:06:11 -05:00
addScanAlso ( nextType ) ;
}
2014-03-10 12:43:49 -04:00
RuntimeChildResourceDefinition def = new RuntimeChildResourceDefinition ( next , elementName , childAnnotation , descriptionAnnotation , refTypesList ) ;
2014-02-23 18:13:59 -05:00
orderMap . put ( order , def ) ;
2014-02-18 18:10:50 -05:00
2014-02-20 12:13:05 -05:00
} else if ( IResourceBlock . class . isAssignableFrom ( nextElementType ) ) {
/ *
2014-05-28 16:07:53 -04:00
* Child is a resource block ( i . e . a sub - tag within a resource ) TODO : do these have a better name
* according to HL7 ?
2014-02-20 12:13:05 -05:00
* /
2014-02-21 21:06:11 -05:00
Class < ? extends IResourceBlock > blockDef = ( Class < ? extends IResourceBlock > ) nextElementType ;
addScanAlso ( blockDef ) ;
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
2014-05-28 16:07:53 -04:00
} else if ( IDatatype . class . equals ( nextElementType ) | | IElement . 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 ) ;
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-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 ) ;
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-03-10 18:17:30 -04:00
if ( nextElementType . equals ( BoundCodeableConceptDt . class ) ) {
IValueSetEnumBinder < Enum < ? > > binder = getBoundCodeBinder ( next ) ;
def = new RuntimeChildCompositeBoundDatatypeDefinition ( next , elementName , childAnnotation , descriptionAnnotation , nextDatatype , binder ) ;
2014-03-24 08:32:28 -04:00
} else if ( NarrativeDt . class . getSimpleName ( ) . equals ( nextElementType . getSimpleName ( ) ) ) {
def = new RuntimeChildNarrativeDefinition ( next , elementName , childAnnotation , descriptionAnnotation , nextDatatype ) ;
2014-03-10 18:17:30 -04:00
} else {
def = new RuntimeChildCompositeDatatypeDefinition ( next , elementName , childAnnotation , descriptionAnnotation , nextDatatype ) ;
}
2014-02-18 18:10:50 -05:00
}
CodeableConceptElement concept = next . getAnnotation ( CodeableConceptElement . class ) ;
if ( concept ! = null ) {
if ( ! ICodedDatatype . class . isAssignableFrom ( nextDatatype ) ) {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException ( " Field ' " + elementName + " ' in type ' " + theClass . getCanonicalName ( ) + " ' is marked as @ " + CodeableConceptElement . class . getCanonicalName ( ) + " but type is not a subtype of " + ICodedDatatype . class . getName ( ) ) ;
2014-02-18 18:10:50 -05:00
} else {
Class < ? extends ICodeEnum > type = concept . type ( ) ;
2014-02-19 11:59:12 -05:00
myScanAlsoCodeTable . add ( type ) ;
2014-02-18 18:10:50 -05:00
def . setCodeType ( type ) ;
}
2014-02-17 17:58:32 -05:00
}
2014-02-18 08:26:49 -05:00
2014-02-23 18:13:59 -05:00
orderMap . put ( order , def ) ;
2014-02-18 18:10:50 -05:00
2014-02-20 12:13:05 -05:00
} else {
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 ) ;
}
}
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 {
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 ;
}
private String scanResource ( Class < ? extends IResource > theClass , ResourceDef resourceDefinition ) {
ourLog . debug ( " Scanning resource class: {} " , theClass . getName ( ) ) ;
String resourceName = resourceDefinition . name ( ) ;
if ( isBlank ( resourceName ) ) {
throw new ConfigurationException ( " Resource type @ " + ResourceDef . class . getSimpleName ( ) + " annotation contains no resource name: " + theClass . getCanonicalName ( ) ) ;
}
if ( myNameToResourceDefinitions . containsKey ( resourceName ) ) {
if ( ! myNameToResourceDefinitions . get ( resourceName ) . getImplementingClass ( ) . equals ( theClass ) ) {
2014-03-17 08:57:57 -04:00
// throw new
// ConfigurationException("Detected duplicate element name '" +
// resourceName + "' in types '" + theClass.getCanonicalName() +
// "' and '"
// +
// myNameToResourceDefinitions.get(resourceName).getImplementingClass()
// + "'");
2014-03-13 17:33:12 -04:00
} else {
return resourceName ;
2014-02-22 15:33:02 -05:00
}
}
2014-03-11 08:39:55 -04:00
String resourceId = resourceDefinition . id ( ) ;
if ( isBlank ( resourceId ) ) {
2014-03-17 08:57:57 -04:00
// throw new ConfigurationException("Resource type @" +
// ResourceDef.class.getSimpleName() +
// " annotation contains no resource ID: " +
// theClass.getCanonicalName());
2014-03-13 17:33:12 -04:00
} else {
if ( myIdToResourceDefinition . containsKey ( resourceId ) ) {
2014-03-17 08:57:57 -04:00
throw new ConfigurationException ( " The following resource types have the same ID of ' " + resourceId + " ' - " + theClass . getCanonicalName ( ) + " and " + myIdToResourceDefinition . get ( resourceId ) . getImplementingClass ( ) . getCanonicalName ( ) ) ;
2014-03-13 17:33:12 -04:00
}
2014-03-11 08:39:55 -04:00
}
2014-03-13 17:33:12 -04:00
2014-03-10 12:43:49 -04:00
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition ( theClass , resourceDefinition ) ;
2014-02-22 15:33:02 -05:00
myClassToElementDefinitions . put ( theClass , resourceDef ) ;
myNameToResourceDefinitions . put ( resourceName , resourceDef ) ;
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-05-05 05:57:43 -07:00
private void scanResourceForSearchParams ( Class < ? extends IResource > theClass , RuntimeResourceDefinition theResourceDef ) {
2014-05-07 08:18:43 -04:00
2014-05-05 05:57:43 -07:00
for ( Field nextField : theClass . getFields ( ) ) {
SearchParamDefinition searchParam = nextField . getAnnotation ( SearchParamDefinition . class ) ;
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 ) {
throw new ConfigurationException ( " Searc param " + searchParam . name ( ) + " has an invalid type: " + searchParam . type ( ) ) ;
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-05-07 08:18:43 -04:00
2014-05-05 05:57:43 -07:00
}
2014-03-11 08:39:55 -04:00
public Map < String , RuntimeResourceDefinition > getIdToResourceDefinition ( ) {
return myIdToResourceDefinition ;
}
private static Class < ? > getGenericCollectionTypeOfCodedField ( Field next ) {
Class < ? > type ;
ParameterizedType collectionType = ( ParameterizedType ) next . getGenericType ( ) ;
Type firstArg = collectionType . getActualTypeArguments ( ) [ 0 ] ;
if ( ParameterizedType . class . isAssignableFrom ( firstArg . getClass ( ) ) ) {
ParameterizedType pt = ( ( ParameterizedType ) firstArg ) ;
firstArg = pt . getActualTypeArguments ( ) [ 0 ] ;
type = ( Class < ? > ) firstArg ;
} else {
type = ( Class < ? > ) firstArg ;
}
return type ;
}
2014-02-17 17:58:32 -05:00
}