001package ca.uhn.fhir.context; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2022 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022import static org.apache.commons.lang3.StringUtils.isNotBlank; 023 024import java.lang.reflect.Field; 025import java.lang.reflect.Modifier; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.List; 030import java.util.Map; 031 032import org.hl7.fhir.instance.model.api.IBase; 033 034import ca.uhn.fhir.model.api.IElement; 035import ca.uhn.fhir.model.api.annotation.Child; 036import ca.uhn.fhir.model.api.annotation.Description; 037import ca.uhn.fhir.model.api.annotation.Extension; 038import ca.uhn.fhir.util.ReflectionUtil; 039 040public class RuntimeChildDeclaredExtensionDefinition extends RuntimeChildChoiceDefinition { 041 042 private boolean myDefinedLocally; 043 private String myExtensionUrl; 044 private boolean myModifier; 045 private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToChildExtension; 046 private volatile Object myInstanceConstructorArguments; 047 private Class<?> myEnumerationType; 048 private Class<? extends IBase> myChildType; 049 private RuntimeResourceBlockDefinition myChildResourceBlock; 050 private BaseRuntimeElementDefinition<?> myChildDef; 051 052 /** 053 * @param theBoundTypeBinder 054 * If the child is of a type that requires a constructor argument to instantiate, this is the argument to 055 * use 056 * @param theDefinedLocally 057 * See {@link Extension#definedLocally()} 058 */ 059 RuntimeChildDeclaredExtensionDefinition(Field theField, Child theChild, Description theDescriptionAnnotation, Extension theExtension, String theElementName, String theExtensionUrl, 060 Class<? extends IBase> theChildType, Object theBoundTypeBinder) 061 throws ConfigurationException { 062 super(theField, theElementName, theChild, theDescriptionAnnotation); 063 assert isNotBlank(theExtensionUrl); 064 myExtensionUrl = theExtensionUrl; 065 myChildType = theChildType; 066 myDefinedLocally = theExtension.definedLocally(); 067 myModifier = theExtension.isModifier(); 068 myInstanceConstructorArguments = theBoundTypeBinder; 069 070 List<Class<? extends IBase>> choiceTypes = new ArrayList<Class<? extends IBase>>(); 071 for (Class<? extends IElement> next : theChild.type()) { 072 choiceTypes.add(next); 073 } 074 075 if (Modifier.isAbstract(theChildType.getModifiers()) == false) { 076 choiceTypes.add(theChildType); 077 } 078 079 setChoiceTypes(choiceTypes); 080 } 081 082 @Override 083 public String getElementName() { 084 return "value"; 085 } 086 087 @Override 088 public Object getInstanceConstructorArguments() { 089 Object retVal = myInstanceConstructorArguments; 090 if (retVal == null && myEnumerationType != null) { 091 retVal = RuntimeChildPrimitiveEnumerationDatatypeDefinition.toEnumFactory(myEnumerationType); 092 myInstanceConstructorArguments = retVal; 093 } 094 return retVal; 095 } 096 097 public void setEnumerationType(Class<?> theEnumerationType) { 098 myEnumerationType = theEnumerationType; 099 } 100 101 @Override 102 public String getChildNameByDatatype(Class<? extends IBase> theDatatype) { 103 104 String retVal = super.getChildNameByDatatype(theDatatype); 105 106 if (retVal != null) { 107 BaseRuntimeElementDefinition<?> childDef = super.getChildElementDefinitionByDatatype(theDatatype); 108 if (childDef instanceof RuntimeResourceBlockDefinition) { 109 // Child is a newted extension 110 retVal = null; 111 } 112 } 113 114 if (retVal == null) { 115 if (myModifier) { 116 return "modifierExtension"; 117 } 118 return "extension"; 119 120 } 121 return retVal; 122 } 123 124 @Override 125 public BaseRuntimeElementDefinition<?> getChildByName(String theName) { 126 String name = theName; 127 if ("extension".equals(name)||"modifierExtension".equals(name)) { 128 if (myChildResourceBlock != null) { 129 return myChildResourceBlock; 130 } 131 if (myChildDef != null) { 132 return myChildDef; 133 } 134 } 135 136 if (getValidChildNames().contains(name) == false) { 137 return null; 138 } 139 140 return super.getChildByName(name); 141 } 142 143 @Override 144 public BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IBase> theDatatype) { 145 if (myChildResourceBlock != null) { 146 if (myChildResourceBlock.getImplementingClass().equals(theDatatype)) { 147 return myChildResourceBlock; 148 } 149 } 150 return super.getChildElementDefinitionByDatatype(theDatatype); 151 } 152 153 public RuntimeChildDeclaredExtensionDefinition getChildExtensionForUrl(String theUrl) { 154 return myUrlToChildExtension.get(theUrl); 155 } 156 157 @Override 158 public String getExtensionUrl() { 159 return myExtensionUrl; 160 } 161 162 public boolean isDefinedLocally() { 163 return myDefinedLocally; 164 } 165 166 @Override 167 public boolean isModifier() { 168 return myModifier; 169 } 170 171 @Override 172 void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) { 173 myUrlToChildExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>(); 174 175 BaseRuntimeElementDefinition<?> elementDef = theClassToElementDefinitions.get(myChildType); 176 177 /* 178 * This will happen for any type that isn't defined in the base set of 179 * built-in types, e.g. custom structures or custom extensions 180 */ 181 if (elementDef == null) { 182 if (Modifier.isAbstract(myChildType.getModifiers()) == false) { 183 elementDef = theContext.getElementDefinition(myChildType); 184 } 185 } 186 187 if (elementDef instanceof RuntimePrimitiveDatatypeDefinition || elementDef instanceof RuntimeCompositeDatatypeDefinition) { 188// myDatatypeChildName = "value" + elementDef.getName().substring(0, 1).toUpperCase() + elementDef.getName().substring(1); 189// if ("valueResourceReference".equals(myDatatypeChildName)) { 190 // Per one of the examples here: http://hl7.org/implement/standards/fhir/extensibility.html#extension 191// myDatatypeChildName = "valueResource"; 192// List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>(); 193// types.add(IBaseResource.class); 194// myChildDef = findResourceReferenceDefinition(theClassToElementDefinitions); 195// } else { 196 myChildDef = elementDef; 197// } 198 } else if (elementDef instanceof RuntimeResourceBlockDefinition) { 199 RuntimeResourceBlockDefinition extDef = ((RuntimeResourceBlockDefinition) elementDef); 200 for (RuntimeChildDeclaredExtensionDefinition next : extDef.getExtensions()) { 201 myUrlToChildExtension.put(next.getExtensionUrl(), next); 202 } 203 myChildResourceBlock = (RuntimeResourceBlockDefinition) elementDef; 204 } 205 206 myUrlToChildExtension = Collections.unmodifiableMap(myUrlToChildExtension); 207 208 super.sealAndInitialize(theContext, theClassToElementDefinitions); 209 } 210 211 public IBase newInstance() { 212 return ReflectionUtil.newInstance(myChildType); 213 } 214 215 public Class<? extends IBase> getChildType() { 216 return myChildType; 217 } 218 219}