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