001package ca.uhn.fhir.context; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2017 University Health Network 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 java.util.ArrayList; 024import java.util.Collections; 025import java.util.Comparator; 026import java.util.HashMap; 027import java.util.LinkedHashMap; 028import java.util.List; 029import java.util.Map; 030 031import org.apache.commons.lang3.StringUtils; 032import org.hl7.fhir.instance.model.api.IAnyResource; 033import org.hl7.fhir.instance.model.api.IBase; 034import org.hl7.fhir.instance.model.api.IBaseResource; 035import org.hl7.fhir.instance.model.api.IDomainResource; 036 037import ca.uhn.fhir.model.api.annotation.ResourceDef; 038import ca.uhn.fhir.util.UrlUtil; 039 040public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefinition<IBaseResource> { 041 042 private Class<? extends IBaseResource> myBaseType; 043 private Map<String, List<RuntimeSearchParam>> myCompartmentNameToSearchParams; 044 private FhirContext myContext; 045 private String myId; 046 private Map<String, RuntimeSearchParam> myNameToSearchParam = new LinkedHashMap<String, RuntimeSearchParam>(); 047 private IBaseResource myProfileDef; 048 private String myResourceProfile; 049 private List<RuntimeSearchParam> mySearchParams; 050 private final FhirVersionEnum myStructureVersion; 051 private volatile RuntimeResourceDefinition myBaseDefinition; 052 053 054 055 public RuntimeResourceDefinition(FhirContext theContext, String theResourceName, Class<? extends IBaseResource> theClass, ResourceDef theResourceAnnotation, boolean theStandardType, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) { 056 super(theResourceName, theClass, theStandardType, theContext, theClassToElementDefinitions); 057 myContext = theContext; 058 myResourceProfile = theResourceAnnotation.profile(); 059 myId = theResourceAnnotation.id(); 060 061 IBaseResource instance; 062 try { 063 instance = theClass.newInstance(); 064 } catch (Exception e) { 065 throw new ConfigurationException(myContext.getLocalizer().getMessage(getClass(), "nonInstantiableType", theClass.getName(), e.toString()), e); 066 } 067 myStructureVersion = instance.getStructureFhirVersionEnum(); 068 if (myStructureVersion != theContext.getVersion().getVersion()) { 069 throw new ConfigurationException(myContext.getLocalizer().getMessage(getClass(), "typeWrongVersion", theContext.getVersion().getVersion(), theClass.getName(), myStructureVersion)); 070 } 071 072 } 073 074 075 public void addSearchParam(RuntimeSearchParam theParam) { 076 myNameToSearchParam.put(theParam.getName(), theParam); 077 } 078 079 /** 080 * If this definition refers to a class which extends another resource definition type, this 081 * method will return the definition of the topmost resource. For example, if this definition 082 * refers to MyPatient2, which extends MyPatient, which in turn extends Patient, this method 083 * will return the resource definition for Patient. 084 * <p> 085 * If the definition has no parent, returns <code>this</code> 086 * </p> 087 */ 088 public RuntimeResourceDefinition getBaseDefinition() { 089 validateSealed(); 090 if (myBaseDefinition == null) { 091 myBaseDefinition = myContext.getResourceDefinition(myBaseType); 092 } 093 return myBaseDefinition; 094 } 095 096 @Override 097 public ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum getChildType() { 098 return ChildTypeEnum.RESOURCE; 099 } 100 101 public String getId() { 102 return myId; 103 } 104 105 /** 106 * Express {@link #getImplementingClass()} as theClass (to prevent casting warnings) 107 */ 108 @SuppressWarnings("unchecked") 109 public <T> Class<T> getImplementingClass(Class<T> theClass) { 110 if (!theClass.isAssignableFrom(getImplementingClass())) { 111 throw new ConfigurationException("Unable to convert " + getImplementingClass() + " to " + theClass); 112 } 113 return (Class<T>) getImplementingClass(); 114 } 115 116 @Deprecated 117 public String getResourceProfile() { 118 return myResourceProfile; 119 } 120 121 public String getResourceProfile(String theServerBase) { 122 validateSealed(); 123 String profile; 124 if (!myResourceProfile.isEmpty()) { 125 profile = myResourceProfile; 126 } else if (!myId.isEmpty()) { 127 profile = myId; 128 } else { 129 return ""; 130 } 131 132 if (!UrlUtil.isValid(profile)) { 133 String resourceName = "/StructureDefinition/"; 134 if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { 135 resourceName = "/Profile/"; 136 } 137 String profileWithUrl = theServerBase + resourceName + profile; 138 if (UrlUtil.isValid(profileWithUrl)) { 139 return profileWithUrl; 140 } 141 } 142 return profile; 143 } 144 145 public RuntimeSearchParam getSearchParam(String theName) { 146 validateSealed(); 147 return myNameToSearchParam.get(theName); 148 } 149 150 public List<RuntimeSearchParam> getSearchParams() { 151 validateSealed(); 152 return mySearchParams; 153 } 154 155 /** 156 * Will not return null 157 */ 158 public List<RuntimeSearchParam> getSearchParamsForCompartmentName(String theCompartmentName) { 159 validateSealed(); 160 List<RuntimeSearchParam> retVal = myCompartmentNameToSearchParams.get(theCompartmentName); 161 if (retVal == null) { 162 return Collections.emptyList(); 163 } 164 return retVal; 165 } 166 167 public FhirVersionEnum getStructureVersion() { 168 return myStructureVersion; 169 } 170 171 public boolean isBundle() { 172 return "Bundle".equals(getName()); 173 } 174 175 @SuppressWarnings("unchecked") 176 @Override 177 public void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) { 178 super.sealAndInitialize(theContext, theClassToElementDefinitions); 179 180 myNameToSearchParam = Collections.unmodifiableMap(myNameToSearchParam); 181 182 ArrayList<RuntimeSearchParam> searchParams = new ArrayList<RuntimeSearchParam>(myNameToSearchParam.values()); 183 Collections.sort(searchParams, new Comparator<RuntimeSearchParam>() { 184 @Override 185 public int compare(RuntimeSearchParam theArg0, RuntimeSearchParam theArg1) { 186 return theArg0.getName().compareTo(theArg1.getName()); 187 } 188 }); 189 mySearchParams = Collections.unmodifiableList(searchParams); 190 191 Map<String, List<RuntimeSearchParam>> compartmentNameToSearchParams = new HashMap<String, List<RuntimeSearchParam>>(); 192 for (RuntimeSearchParam next : searchParams) { 193 if (next.getProvidesMembershipInCompartments() != null) { 194 for (String nextCompartment : next.getProvidesMembershipInCompartments()) { 195 if (!compartmentNameToSearchParams.containsKey(nextCompartment)) { 196 compartmentNameToSearchParams.put(nextCompartment, new ArrayList<RuntimeSearchParam>()); 197 } 198 compartmentNameToSearchParams.get(nextCompartment).add(next); 199 } 200 } 201 } 202 myCompartmentNameToSearchParams = Collections.unmodifiableMap(compartmentNameToSearchParams); 203 204 Class<?> target = getImplementingClass(); 205 myBaseType = (Class<? extends IBaseResource>) target; 206 do { 207 target = target.getSuperclass(); 208 if (IBaseResource.class.isAssignableFrom(target) && target.getAnnotation(ResourceDef.class) != null) { 209 myBaseType = (Class<? extends IBaseResource>) target; 210 } 211 } while (target.equals(Object.class) == false); 212 213 /* 214 * See #504: 215 * Bundle types may not have extensions 216 */ 217 if (hasExtensions()) { 218 if (IAnyResource.class.isAssignableFrom(getImplementingClass())) { 219 if (!IDomainResource.class.isAssignableFrom(getImplementingClass())) { 220 throw new ConfigurationException("Class \"" + getImplementingClass() + "\" is invalid. This resource type is not a DomainResource, it must not have extensions"); 221 } 222 } 223 } 224 225 } 226 227 @Deprecated 228 public synchronized IBaseResource toProfile() { 229 validateSealed(); 230 if (myProfileDef != null) { 231 return myProfileDef; 232 } 233 234 IBaseResource retVal = myContext.getVersion().generateProfile(this, null); 235 myProfileDef = retVal; 236 237 return retVal; 238 } 239 240 public synchronized IBaseResource toProfile(String theServerBase) { 241 validateSealed(); 242 if (myProfileDef != null) { 243 return myProfileDef; 244 } 245 246 IBaseResource retVal = myContext.getVersion().generateProfile(this, theServerBase); 247 myProfileDef = retVal; 248 249 return retVal; 250 } 251 252}