View Javadoc
1   package ca.uhn.fhir.context;
2   
3   import static org.apache.commons.lang3.StringUtils.isNotBlank;
4   
5   import java.lang.reflect.Field;
6   import java.lang.reflect.Modifier;
7   
8   /*
9    * #%L
10   * HAPI FHIR - Core Library
11   * %%
12   * Copyright (C) 2014 - 2018 University Health Network
13   * %%
14   * Licensed under the Apache License, Version 2.0 (the "License");
15   * you may not use this file except in compliance with the License.
16   * You may obtain a copy of the License at
17   * 
18   *      http://www.apache.org/licenses/LICENSE-2.0
19   * 
20   * Unless required by applicable law or agreed to in writing, software
21   * distributed under the License is distributed on an "AS IS" BASIS,
22   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23   * See the License for the specific language governing permissions and
24   * limitations under the License.
25   * #L%
26   */
27  
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.LinkedList;
33  import java.util.List;
34  import java.util.ListIterator;
35  import java.util.Map;
36  import java.util.Map.Entry;
37  import java.util.Set;
38  import java.util.TreeMap;
39  import java.util.TreeSet;
40  
41  import org.hl7.fhir.instance.model.api.IAnyResource;
42  import org.hl7.fhir.instance.model.api.IBase;
43  import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
44  import org.hl7.fhir.instance.model.api.IBaseDatatype;
45  import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
46  import org.hl7.fhir.instance.model.api.IBaseEnumeration;
47  import org.hl7.fhir.instance.model.api.IBaseExtension;
48  import org.hl7.fhir.instance.model.api.IBaseReference;
49  import org.hl7.fhir.instance.model.api.IBaseResource;
50  import org.hl7.fhir.instance.model.api.ICompositeType;
51  import org.hl7.fhir.instance.model.api.INarrative;
52  import org.hl7.fhir.instance.model.api.IPrimitiveType;
53  
54  import ca.uhn.fhir.model.api.IBoundCodeableConcept;
55  import ca.uhn.fhir.model.api.IDatatype;
56  import ca.uhn.fhir.model.api.IElement;
57  import ca.uhn.fhir.model.api.IResource;
58  import ca.uhn.fhir.model.api.IResourceBlock;
59  import ca.uhn.fhir.model.api.IValueSetEnumBinder;
60  import ca.uhn.fhir.model.api.annotation.Binding;
61  import ca.uhn.fhir.model.api.annotation.Child;
62  import ca.uhn.fhir.model.api.annotation.ChildOrder;
63  import ca.uhn.fhir.model.api.annotation.Description;
64  import ca.uhn.fhir.model.api.annotation.Extension;
65  import ca.uhn.fhir.model.base.composite.BaseContainedDt;
66  import ca.uhn.fhir.model.base.composite.BaseNarrativeDt;
67  import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
68  import ca.uhn.fhir.model.primitive.BoundCodeDt;
69  import ca.uhn.fhir.parser.DataFormatException;
70  import ca.uhn.fhir.util.ReflectionUtil;
71  
72  public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> extends BaseRuntimeElementDefinition<T> {
73  
74  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseRuntimeElementCompositeDefinition.class);
75  	private Map<String, Integer> forcedOrder = null;
76  	private List<BaseRuntimeChildDefinition> myChildren = new ArrayList<BaseRuntimeChildDefinition>();
77  	private List<BaseRuntimeChildDefinition> myChildrenAndExtensions;
78  	private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions;
79  	private FhirContext myContext;
80  	private Map<String, BaseRuntimeChildDefinition> myNameToChild = new HashMap<String, BaseRuntimeChildDefinition>();
81  	private List<ScannedField> myScannedFields = new ArrayList<BaseRuntimeElementCompositeDefinition.ScannedField>();
82  	private volatile boolean mySealed;
83  
84  	@SuppressWarnings("unchecked")
85  	public BaseRuntimeElementCompositeDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType, FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
86  		super(theName, theImplementingClass, theStandardType);
87  		
88  		myContext = theContext;
89  		myClassToElementDefinitions = theClassToElementDefinitions;
90  		
91  		/*
92  		 * We scan classes for annotated fields in the class but also all of its superclasses
93  		 */
94  		Class<? extends IBase> current = theImplementingClass;
95  		LinkedList<Class<? extends IBase>> classes = new LinkedList<Class<? extends IBase>>();
96  		do {
97  			if (forcedOrder == null) {
98  				ChildOrder childOrder = current.getAnnotation(ChildOrder.class);
99  				if (childOrder != null) {
100 					forcedOrder = new HashMap<String, Integer>();
101 					for (int i = 0; i < childOrder.names().length; i++) {
102 						String nextName = childOrder.names()[i];
103 						if (nextName.endsWith("[x]")) {
104 							nextName = nextName.substring(0, nextName.length() - 3);
105 						}
106 						forcedOrder.put(nextName, i);
107 					}
108 				}
109 			}
110 			classes .push(current);
111 			if (IBase.class.isAssignableFrom(current.getSuperclass())) {
112 				current = (Class<? extends IBase>) current.getSuperclass();
113 			} else {
114 				current = null;
115 			}
116 		} while (current != null);
117 
118 		Set<Field> fields = new HashSet<Field>();
119 		for (Class<? extends IBase> nextClass : classes) {
120 			int fieldIndexInClass = 0;
121 			for (Field next : nextClass.getDeclaredFields()) {
122 				if (fields.add(next)) {
123 					ScannedField scannedField = new ScannedField(next, theImplementingClass, fieldIndexInClass == 0);
124 					if (scannedField.getChildAnnotation() != null) {
125 						myScannedFields.add(scannedField);
126 						fieldIndexInClass++;
127 					}
128 				}
129 			}
130 		}
131 
132 	}
133 
134 	void addChild(BaseRuntimeChildDefinition theNext) {
135 		if (theNext == null) {
136 			throw new NullPointerException();
137 		}
138 		if (theNext.getExtensionUrl() != null) {
139 			throw new IllegalArgumentException("Shouldn't haven an extension URL, use addExtension instead");
140 		}
141 		myChildren.add(theNext);
142 	}
143 
144 	public BaseRuntimeChildDefinition getChildByName(String theName){
145 		validateSealed();
146 		BaseRuntimeChildDefinition retVal = myNameToChild.get(theName);
147 		return retVal;
148 	}
149 
150 	public BaseRuntimeChildDefinition getChildByNameOrThrowDataFormatException(String theName) throws DataFormatException {
151 		validateSealed();
152 		BaseRuntimeChildDefinition retVal = myNameToChild.get(theName);
153 		if (retVal == null) {
154 			throw new DataFormatException("Unknown child name '" + theName + "' in element " + getName() + " - Valid names are: " + new TreeSet<String>(myNameToChild.keySet()));
155 		}
156 		return retVal;
157 	}
158 
159 	public List<BaseRuntimeChildDefinition> getChildren() {
160 		validateSealed();
161 		return myChildren;
162 	}
163 	
164 	
165 	public List<BaseRuntimeChildDefinition> getChildrenAndExtension() {
166 		validateSealed();
167 		return myChildrenAndExtensions;
168 	}
169 
170 
171 	/**
172 	 * Has this class been sealed
173 	 */
174 	public boolean isSealed() {
175 		return mySealed;
176 	}
177 
178 	@SuppressWarnings("unchecked")
179 	void populateScanAlso(Set<Class<? extends IBase>> theScanAlso) {
180 		for (ScannedField next : myScannedFields) {
181 			if (IBase.class.isAssignableFrom(next.getElementType())) {
182 				if (next.getElementType().isInterface() == false && Modifier.isAbstract(next.getElementType().getModifiers()) == false) {
183 					theScanAlso.add((Class<? extends IBase>) next.getElementType());
184 				}
185 			}
186 			for (Class<? extends IBase> nextChildType : next.getChoiceTypes()) {
187 				if (nextChildType.isInterface() == false && Modifier.isAbstract(nextChildType.getModifiers()) == false) {
188 					theScanAlso.add(nextChildType);
189 				}
190 			}
191 		}
192 	}
193 	
194 	private void scanCompositeElementForChildren() {
195 		Set<String> elementNames = new HashSet<String>();
196 		TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToElementDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
197 		TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToExtensionDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
198 
199 		scanCompositeElementForChildren(elementNames, orderToElementDef, orderToExtensionDef);
200 
201 		if (forcedOrder != null) {
202 			/* 
203 			 * Find out how many elements don't match any entry in the list
204 			 * for forced order. Those elements come first.
205 			 */
206 			TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> newOrderToExtensionDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
207 			int unknownCount = 0;
208 			for (BaseRuntimeDeclaredChildDefinition nextEntry : orderToElementDef.values()) {
209 				if (!forcedOrder.containsKey(nextEntry.getElementName())) {
210 					newOrderToExtensionDef.put(unknownCount, nextEntry);
211 					unknownCount++;
212 				}
213 			}
214 			for (BaseRuntimeDeclaredChildDefinition nextEntry : orderToElementDef.values()) {
215 				if (forcedOrder.containsKey(nextEntry.getElementName())) {
216 					Integer newOrder = forcedOrder.get(nextEntry.getElementName());
217 					newOrderToExtensionDef.put(newOrder + unknownCount, nextEntry);
218 				}
219 			}
220 			orderToElementDef = newOrderToExtensionDef;
221 		}
222 		
223 		TreeSet<Integer> orders = new TreeSet<Integer>();
224 		orders.addAll(orderToElementDef.keySet());
225 		orders.addAll(orderToExtensionDef.keySet());
226 
227 		for (Integer i : orders) {
228 			BaseRuntimeChildDefinition nextChild = orderToElementDef.get(i);
229 			if (nextChild != null) {
230 				this.addChild(nextChild);
231 			}
232 			BaseRuntimeDeclaredChildDefinition nextExt = orderToExtensionDef.get(i);
233 			if (nextExt != null) {
234 				this.addExtension((RuntimeChildDeclaredExtensionDefinition) nextExt);
235 			}
236 		}
237 
238 	}
239 	
240 	@SuppressWarnings("unchecked")
241 	private void scanCompositeElementForChildren(Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
242 			TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
243 		int baseElementOrder = 0; 
244 
245 		for (ScannedField next : myScannedFields) {
246 			if (next.isFirstFieldInNewClass()) {
247 				baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
248 			}
249 			
250 			Class<?> declaringClass = next.getField().getDeclaringClass(); 
251 
252 			Description descriptionAnnotation = ModelScanner.pullAnnotation(next.getField(), Description.class);
253 
254 			TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
255 			Extension extensionAttr = ModelScanner.pullAnnotation(next.getField(), Extension.class);
256 			if (extensionAttr != null) {
257 				orderMap = theOrderToExtensionDef;
258 			}
259 
260 			Child childAnnotation = next.getChildAnnotation();
261 			Field nextField = next.getField();
262 			String elementName = childAnnotation.name();
263 			int order = childAnnotation.order();
264 			boolean childIsChoiceType = false;
265 			boolean orderIsReplaceParent = false;
266 			
267 			if (order == Child.REPLACE_PARENT) {
268 				
269 				if (extensionAttr != null) {
270 
271 					for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
272 						BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
273 						if (nextDef instanceof RuntimeChildDeclaredExtensionDefinition) {
274 							if (nextDef.getExtensionUrl().equals(extensionAttr.url())) {
275 								orderIsReplaceParent = true;
276 								order = nextEntry.getKey();
277 								orderMap.remove(nextEntry.getKey());
278 								elementNames.remove(elementName);
279 								break;
280 							}
281 						}
282 					}
283 					if (order == Child.REPLACE_PARENT) {
284 						throw new ConfigurationException("Field " + nextField.getName() + "' on target type " + declaringClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
285 								+ ") but no parent element with extension URL " + extensionAttr.url() + " could be found on type " + nextField.getDeclaringClass().getSimpleName());
286 					}
287 
288 				} else {
289 
290 					for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
291 						BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
292 						if (elementName.equals(nextDef.getElementName())) {
293 							orderIsReplaceParent = true;
294 							order = nextEntry.getKey();
295 							BaseRuntimeDeclaredChildDefinition existing = orderMap.remove(nextEntry.getKey());
296 							elementNames.remove(elementName);
297 							
298 							/*
299 							 * See #350 - If the original field (in the superclass) with the given name is a choice, then we need to make sure
300 							 * that the field which replaces is a choice even if it's only a choice of one type - this is because the
301 							 * element name when serialized still needs to reflect the datatype
302 							 */
303 							if (existing instanceof RuntimeChildChoiceDefinition) {
304 								childIsChoiceType = true;
305 							}
306 							break;
307 						}
308 					}
309 					if (order == Child.REPLACE_PARENT) {
310 						throw new ConfigurationException("Field " + nextField.getName() + "' on target type " + declaringClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
311 								+ ") but no parent element with name " + elementName + " could be found on type " + nextField.getDeclaringClass().getSimpleName());
312 					}
313 
314 				}
315 
316 			}
317 			
318 			if (order < 0 && order != Child.ORDER_UNKNOWN) {
319 				throw new ConfigurationException("Invalid order '" + order + "' on @Child for field '" + nextField.getName() + "' on target type: " + declaringClass);
320 			}
321 			
322 			if (order != Child.ORDER_UNKNOWN && !orderIsReplaceParent) {
323 				order = order + baseElementOrder;
324 			}
325 			// int min = childAnnotation.min();
326 			// int max = childAnnotation.max();
327 
328 			/*
329 			 * 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
330 			 */
331 			if (order == Child.ORDER_UNKNOWN) {
332 				order = Integer.valueOf(0);
333 				while (orderMap.containsKey(order)) {
334 					order++;
335 				}
336 			}
337 
338 			List<Class<? extends IBase>> choiceTypes = next.getChoiceTypes();
339 			
340 			if (orderMap.containsKey(order)) {
341 				throw new ConfigurationException("Detected duplicate field order '" + childAnnotation.order() + "' for element named '" + elementName + "' in type '" + declaringClass.getCanonicalName() + "' - Already had: " + orderMap.get(order).getElementName());
342 			}
343 
344 			if (elementNames.contains(elementName)) {
345 				throw new ConfigurationException("Detected duplicate field name '" + elementName + "' in type '" + declaringClass.getCanonicalName() + "'");
346 			}
347 
348 			Class<?> nextElementType = next.getElementType();
349 
350 			BaseRuntimeDeclaredChildDefinition def;
351 			if (childAnnotation.name().equals("extension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
352 				def = new RuntimeChildExtension(nextField, childAnnotation.name(), childAnnotation, descriptionAnnotation);
353 			} else if (childAnnotation.name().equals("modifierExtension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
354 				def = new RuntimeChildExtension(nextField, childAnnotation.name(), childAnnotation, descriptionAnnotation);
355 			} else if (BaseContainedDt.class.isAssignableFrom(nextElementType) || (childAnnotation.name().equals("contained") && IBaseResource.class.isAssignableFrom(nextElementType))) {
356 				/*
357 				 * Child is contained resources
358 				 */
359 				def = new RuntimeChildContainedResources(nextField, childAnnotation, descriptionAnnotation, elementName);
360 			} else if (IAnyResource.class.isAssignableFrom(nextElementType) || IResource.class.equals(nextElementType)) {
361 				/*
362 				 * Child is a resource as a direct child, as in Bundle.entry.resource
363 				 */
364 				def = new RuntimeChildDirectResource(nextField, childAnnotation, descriptionAnnotation, elementName);
365 			} else {
366 				childIsChoiceType |= choiceTypes.size() > 1;
367 				if (extensionAttr == null && childIsChoiceType && !BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) && !IBaseReference.class.isAssignableFrom(nextElementType)) {
368 					def = new RuntimeChildChoiceDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
369 				} else if (extensionAttr != null) {
370 					/*
371 					 * Child is an extension
372 					 */
373 					Class<? extends IBase> et = (Class<? extends IBase>) nextElementType;
374 
375 					Object binder = null;
376 					if (BoundCodeDt.class.isAssignableFrom(nextElementType) || IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
377 						binder = ModelScanner.getBoundCodeBinder(nextField);
378 					}
379 
380 					def = new RuntimeChildDeclaredExtensionDefinition(nextField, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et,							binder);
381 
382 					if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
383 						((RuntimeChildDeclaredExtensionDefinition)def).setEnumerationType(ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(nextField));
384 					}
385 				} else if (BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) || IBaseReference.class.isAssignableFrom(nextElementType)) {
386 					/*
387 					 * Child is a resource reference
388 					 */
389 					List<Class<? extends IBaseResource>> refTypesList = new ArrayList<Class<? extends IBaseResource>>();
390 					for (Class<? extends IElement> nextType : childAnnotation.type()) {
391 						if (IBaseReference.class.isAssignableFrom(nextType)) {
392 							refTypesList.add(myContext.getVersion().getVersion().isRi() ? IAnyResource.class : IResource.class);
393 							continue;
394 						} else if (IBaseResource.class.isAssignableFrom(nextType) == false) {
395 							throw new ConfigurationException("Field '" + nextField.getName() + "' in class '" + nextField.getDeclaringClass().getCanonicalName() + "' is of type " + BaseResourceReferenceDt.class + " but contains a non-resource type: " + nextType.getCanonicalName());
396 						}
397 						refTypesList.add((Class<? extends IBaseResource>) nextType);
398 					}
399 					def = new RuntimeChildResourceDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, refTypesList);
400 
401 				} else if (IResourceBlock.class.isAssignableFrom(nextElementType) || IBaseBackboneElement.class.isAssignableFrom(nextElementType)
402 						|| IBaseDatatypeElement.class.isAssignableFrom(nextElementType)) {
403 					/*
404 					 * Child is a resource block (i.e. a sub-tag within a resource) TODO: do these have a better name according to HL7?
405 					 */
406 
407 					Class<? extends IBase> blockDef = (Class<? extends IBase>) nextElementType;
408 					def = new RuntimeChildResourceBlockDefinition(myContext, nextField, childAnnotation, descriptionAnnotation, elementName, blockDef);
409 				} else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType) || "Type".equals(nextElementType.getSimpleName())
410 						|| IBaseDatatype.class.equals(nextElementType)) {
411 
412 					def = new RuntimeChildAny(nextField, elementName, childAnnotation, descriptionAnnotation);
413 				} else if (IDatatype.class.isAssignableFrom(nextElementType) || IPrimitiveType.class.isAssignableFrom(nextElementType) || ICompositeType.class.isAssignableFrom(nextElementType)
414 						|| IBaseDatatype.class.isAssignableFrom(nextElementType) || IBaseExtension.class.isAssignableFrom(nextElementType)) {
415 					Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
416 
417 					if (IPrimitiveType.class.isAssignableFrom(nextElementType)) {
418 						if (nextElementType.equals(BoundCodeDt.class)) {
419 							IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(nextField);
420 							Class<? extends Enum<?>> enumType = ModelScanner.determineEnumTypeForBoundField(nextField);
421 							def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
422 						} else if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
423 							Class<? extends Enum<?>> binderType = ModelScanner.determineEnumTypeForBoundField(nextField);
424 							def = new RuntimeChildPrimitiveEnumerationDatatypeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binderType);
425 						} else {
426 							def = new RuntimeChildPrimitiveDatatypeDefinition(nextField, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
427 						}
428 					} else {
429 						if (IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
430 							IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(nextField);
431 							Class<? extends Enum<?>> enumType = ModelScanner.determineEnumTypeForBoundField(nextField);
432 							def = new RuntimeChildCompositeBoundDatatypeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
433 						} else if (BaseNarrativeDt.class.isAssignableFrom(nextElementType) || INarrative.class.isAssignableFrom(nextElementType)) {
434 							def = new RuntimeChildNarrativeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
435 						} else {
436 							def = new RuntimeChildCompositeDatatypeDefinition(nextField, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
437 						}
438 					}
439 
440 				} else {
441 					throw new ConfigurationException("Field '" + elementName + "' in type '" + declaringClass.getCanonicalName() + "' is not a valid child type: " + nextElementType);
442 				}
443 
444 				Binding bindingAnnotation = ModelScanner.pullAnnotation(nextField, Binding.class);
445 				if (bindingAnnotation != null) {
446 					if (isNotBlank(bindingAnnotation.valueSet())) {
447 						def.setBindingValueSet(bindingAnnotation.valueSet());
448 					}
449 				}
450 				
451 			}
452 
453 			orderMap.put(order, def);
454 			elementNames.add(elementName);
455 		}
456 	}
457 	@Override 
458 	void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
459 		if (mySealed) {
460 			return;
461 		}
462 		mySealed = true;
463 
464 		scanCompositeElementForChildren();
465 		
466 		super.sealAndInitialize(theContext, theClassToElementDefinitions);
467 
468 		for (BaseRuntimeChildDefinition next : myChildren) {
469 			next.sealAndInitialize(theContext, theClassToElementDefinitions);
470 		}
471 
472 		myNameToChild = new HashMap<String, BaseRuntimeChildDefinition>();
473 		for (BaseRuntimeChildDefinition next : myChildren) {	
474 			if (next instanceof RuntimeChildChoiceDefinition) {
475 				String key = ((RuntimeChildChoiceDefinition) next).getElementName()+"[x]";
476 				myNameToChild.put(key, next);
477 			}
478 			for (String nextName : next.getValidChildNames()) {
479 				if (myNameToChild.containsKey(nextName)) {
480 					throw new ConfigurationException("Duplicate child name[" + nextName + "] in Element[" + getName() + "]");
481 				}
482 				myNameToChild.put(nextName, next);
483 			}
484 		}
485 
486 		myChildren = Collections.unmodifiableList(myChildren);
487 		myNameToChild = Collections.unmodifiableMap(myNameToChild);
488 		
489 		List<BaseRuntimeChildDefinition> children = new ArrayList<BaseRuntimeChildDefinition>();
490 		children.addAll(myChildren);
491 		
492 		/*
493 		 * Because of the way the type hierarchy works for DSTU2 resources,
494 		 * things end up in the wrong order
495 		 */
496 		if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU2) {
497 			int extIndex = findIndex(children, "extension", false);
498 			int containedIndex = findIndex(children, "contained", false);
499 			if (containedIndex != -1 && extIndex != -1 && extIndex < containedIndex) {
500 				BaseRuntimeChildDefinition extension = children.remove(extIndex);
501 				if (containedIndex > children.size()) {
502 					children.add(extension);
503 				} else {
504 					children.add(containedIndex, extension);
505 				}
506 				int modIndex = findIndex(children, "modifierExtension", false);
507 				if (modIndex < containedIndex) {
508 					extension = children.remove(modIndex);
509 					if (containedIndex > children.size()) {
510 						children.add(extension);
511 					} else {
512 						children.add(containedIndex, extension);
513 					}
514 				}
515 			}
516 		}
517 		
518 		/*
519 		 * Add declared extensions alongside the undeclared ones
520 		 */
521 		if (getExtensionsNonModifier().isEmpty() == false) {
522 			children.addAll(findIndex(children, "extension", true), getExtensionsNonModifier());
523 		}
524 		if (getExtensionsModifier().isEmpty() == false) {
525 			children.addAll(findIndex(children, "modifierExtension", true), getExtensionsModifier());
526 		}
527 		
528 		myChildrenAndExtensions=Collections.unmodifiableList(children);
529 	}
530 
531 	
532 	@Override
533 	protected void validateSealed() {
534 		if (!mySealed) {
535 			synchronized(myContext) {
536 				if(!mySealed) {
537 					sealAndInitialize(myContext, myClassToElementDefinitions);
538 				}
539 			}
540 		}
541 	}
542 
543 	private static int findIndex(List<BaseRuntimeChildDefinition> theChildren, String theName, boolean theDefaultAtEnd) {
544 		int index = theDefaultAtEnd ? theChildren.size() : -1;
545 		for (ListIterator<BaseRuntimeChildDefinition> iter = theChildren.listIterator(); iter.hasNext(); ) {
546 			if (iter.next().getElementName().equals(theName)) {
547 				index = iter.previousIndex();
548 				break;
549 			}
550 		}
551 		return index;
552 	}
553 
554 	private static class ScannedField {
555 		private Child myChildAnnotation;
556 
557 		private List<Class<? extends IBase>> myChoiceTypes = new ArrayList<Class<? extends IBase>>();
558 		private Class<?> myElementType;
559 		private Field myField;
560 		private boolean myFirstFieldInNewClass;
561 		public ScannedField(Field theField, Class<?> theClass, boolean theFirstFieldInNewClass) {
562 			myField = theField;
563 			myFirstFieldInNewClass = theFirstFieldInNewClass;
564 
565 			Child childAnnotation = ModelScanner.pullAnnotation(theField, Child.class);
566 			if (childAnnotation == null) {
567 				ourLog.trace("Ignoring non @Child field {} on target type {}", theField.getName(), theClass);
568 				return;
569 			}
570 			if (Modifier.isFinal(theField.getModifiers())) {
571 				ourLog.trace("Ignoring constant {} on target type {}", theField.getName(), theClass);
572 				return;
573 			}
574 			
575 			myChildAnnotation = childAnnotation;
576 			myElementType = ModelScanner.determineElementType(theField);
577 			
578 			for (Class<? extends IBase> nextChoiceType : childAnnotation.type()) {
579 				myChoiceTypes.add(nextChoiceType);
580 			}
581 		}
582 		
583 		public Child getChildAnnotation() {
584 			return myChildAnnotation;
585 		}
586 
587 		public List<Class<? extends IBase>> getChoiceTypes() {
588 			return myChoiceTypes;
589 		}
590 
591 		public Class<?> getElementType() {
592 			return myElementType;
593 		}
594 
595 		public Field getField() {
596 			return myField;
597 		}
598 
599 		public boolean isFirstFieldInNewClass() {
600 			return myFirstFieldInNewClass;
601 		}
602 	}
603 
604 }