001package ca.uhn.fhir.context; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2021 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 */ 022 023import ca.uhn.fhir.util.UrlUtil; 024import org.apache.commons.lang3.StringUtils; 025import org.apache.commons.lang3.Validate; 026import org.hl7.fhir.instance.model.api.IBase; 027 028import javax.annotation.Nonnull; 029import javax.annotation.Nullable; 030import java.lang.reflect.Constructor; 031import java.util.*; 032 033public abstract class BaseRuntimeElementDefinition<T extends IBase> { 034 035 private static final Class<Void> VOID_CLASS = Void.class; 036 private final Class<? extends T> myImplementingClass; 037 private final String myName; 038 private final boolean myStandardType; 039 private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<>()); 040 private List<RuntimeChildDeclaredExtensionDefinition> myExtensions = new ArrayList<>(); 041 private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsModifier = new ArrayList<>(); 042 private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsNonModifier = new ArrayList<>(); 043 private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToExtension = new HashMap<>(); 044 private BaseRuntimeElementDefinition<?> myRootParentDefinition; 045 046 public BaseRuntimeElementDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType) { 047 assert StringUtils.isNotBlank(theName); 048 assert theImplementingClass != null; 049 050 String name = theName; 051 // TODO: remove this and fix for the model 052 if (name.endsWith("Dt")) { 053 name = name.substring(0, name.length() - 2); 054 } 055 056 057 myName = name; 058 myStandardType = theStandardType; 059 myImplementingClass = theImplementingClass; 060 } 061 062 public void addExtension(@Nonnull RuntimeChildDeclaredExtensionDefinition theExtension) { 063 Validate.notNull(theExtension, "theExtension must not be null"); 064 myExtensions.add(theExtension); 065 } 066 067 public abstract ChildTypeEnum getChildType(); 068 069 public List<BaseRuntimeChildDefinition> getChildren() { 070 return Collections.emptyList(); 071 } 072 073 @SuppressWarnings("unchecked") 074 private Constructor<T> getConstructor(@Nullable Object theArgument) { 075 076 Class<?> argumentType; 077 if (theArgument == null) { 078 argumentType = VOID_CLASS; 079 } else { 080 argumentType = theArgument.getClass(); 081 } 082 083 Constructor<T> retVal = myConstructors.get(argumentType); 084 if (retVal == null) { 085 for (Constructor<?> next : getImplementingClass().getConstructors()) { 086 if (argumentType == VOID_CLASS) { 087 if (next.getParameterTypes().length == 0) { 088 retVal = (Constructor<T>) next; 089 break; 090 } 091 } else if (next.getParameterTypes().length == 1) { 092 if (next.getParameterTypes()[0].isAssignableFrom(argumentType)) { 093 retVal = (Constructor<T>) next; 094 break; 095 } 096 } 097 } 098 if (retVal == null) { 099 throw new ConfigurationException("Class " + getImplementingClass() + " has no constructor with a single argument of type " + argumentType); 100 } 101 myConstructors.put(argumentType, retVal); 102 } 103 return retVal; 104 } 105 106 /** 107 * @return Returns null if none 108 */ 109 public RuntimeChildDeclaredExtensionDefinition getDeclaredExtension(String theExtensionUrl, final String serverBaseUrl) { 110 validateSealed(); 111 RuntimeChildDeclaredExtensionDefinition definition = myUrlToExtension.get(theExtensionUrl); 112 if (definition == null && StringUtils.isNotBlank(serverBaseUrl)) { 113 for (final Map.Entry<String, RuntimeChildDeclaredExtensionDefinition> entry : myUrlToExtension.entrySet()) { 114 final String key = (!UrlUtil.isValid(entry.getKey()) && StringUtils.isNotBlank(serverBaseUrl)) ? serverBaseUrl + entry.getKey() : entry.getKey(); 115 if (key.equals(theExtensionUrl)) { 116 definition = entry.getValue(); 117 break; 118 } 119 } 120 } 121 return definition; 122 } 123 124 public List<RuntimeChildDeclaredExtensionDefinition> getExtensions() { 125 validateSealed(); 126 return myExtensions; 127 } 128 129 public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsModifier() { 130 validateSealed(); 131 return myExtensionsModifier; 132 } 133 134 public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsNonModifier() { 135 validateSealed(); 136 return myExtensionsNonModifier; 137 } 138 139 public Class<? extends T> getImplementingClass() { 140 return myImplementingClass; 141 } 142 143 /** 144 * @return Returns the runtime name for this resource (i.e. the name that 145 * will be used in encoded messages) 146 */ 147 public String getName() { 148 return myName; 149 } 150 151 public boolean hasExtensions() { 152 validateSealed(); 153 return myExtensions.size() > 0; 154 } 155 156 public boolean isStandardType() { 157 return myStandardType; 158 } 159 160 public T newInstance() { 161 return newInstance(null); 162 } 163 164 public T newInstance(Object theArgument) { 165 try { 166 if (theArgument == null) { 167 return getConstructor(null).newInstance(); 168 } 169 return getConstructor(theArgument).newInstance(theArgument); 170 171 } catch (Exception e) { 172 throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e); 173 } 174 } 175 176 public BaseRuntimeElementDefinition<?> getRootParentDefinition() { 177 return myRootParentDefinition; 178 } 179 180 /** 181 * Invoked prior to use to perform any initialization and make object 182 * mutable. 183 * 184 * @param theContext TODO 185 */ 186 void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) { 187 for (BaseRuntimeChildDefinition next : myExtensions) { 188 next.sealAndInitialize(theContext, theClassToElementDefinitions); 189 } 190 191 for (RuntimeChildDeclaredExtensionDefinition next : myExtensions) { 192 String extUrl = next.getExtensionUrl(); 193 if (myUrlToExtension.containsKey(extUrl)) { 194 throw new ConfigurationException("Duplicate extension URL[" + extUrl + "] in Element[" + getName() + "]"); 195 } 196 myUrlToExtension.put(extUrl, next); 197 if (next.isModifier()) { 198 myExtensionsModifier.add(next); 199 } else { 200 myExtensionsNonModifier.add(next); 201 } 202 203 } 204 205 myExtensions = Collections.unmodifiableList(myExtensions); 206 207 Class parent = myImplementingClass; 208 do { 209 BaseRuntimeElementDefinition<?> parentDefinition = theClassToElementDefinitions.get(parent); 210 if (parentDefinition != null) { 211 myRootParentDefinition = parentDefinition; 212 } 213 parent = parent.getSuperclass(); 214 } while (!parent.equals(Object.class)); 215 216 } 217 218 @Override 219 public String toString() { 220 return getClass().getSimpleName() + "[" + getName() + ", " + getImplementingClass().getSimpleName() + "]"; 221 } 222 223 protected void validateSealed() { 224 /* 225 * this does nothing, but BaseRuntimeElementCompositeDefinition 226 * overrides this method to provide functionality because that class 227 * defers the dealing process 228 */ 229 230 } 231 232 public BaseRuntimeChildDefinition getChildByName(String theChildName) { 233 return null; 234 } 235 236 public enum ChildTypeEnum { 237 COMPOSITE_DATATYPE, 238 /** 239 * HL7.org structure style. 240 */ 241 CONTAINED_RESOURCE_LIST, 242 /** 243 * HAPI structure style. 244 */ 245 CONTAINED_RESOURCES, EXTENSION_DECLARED, 246 ID_DATATYPE, 247 PRIMITIVE_DATATYPE, 248 /** 249 * HAPI style. 250 */ 251 PRIMITIVE_XHTML, 252 /** 253 * HL7.org style. 254 */ 255 PRIMITIVE_XHTML_HL7ORG, 256 RESOURCE, 257 RESOURCE_BLOCK, 258 259 UNDECL_EXT, 260 261 } 262 263}