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