View Javadoc
1   package ca.uhn.fhir.context;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Comparator;
26  import java.util.HashMap;
27  import java.util.LinkedHashMap;
28  import java.util.List;
29  import java.util.Map;
30  
31  import org.apache.commons.lang3.StringUtils;
32  import org.hl7.fhir.instance.model.api.IAnyResource;
33  import org.hl7.fhir.instance.model.api.IBase;
34  import org.hl7.fhir.instance.model.api.IBaseResource;
35  import org.hl7.fhir.instance.model.api.IDomainResource;
36  
37  import ca.uhn.fhir.model.api.annotation.ResourceDef;
38  import ca.uhn.fhir.util.UrlUtil;
39  
40  public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefinition<IBaseResource> {
41  
42  	private Class<? extends IBaseResource> myBaseType;
43  	private Map<String, List<RuntimeSearchParam>> myCompartmentNameToSearchParams;
44  	private FhirContext myContext;
45  	private String myId;
46  	private Map<String, RuntimeSearchParam> myNameToSearchParam = new LinkedHashMap<String, RuntimeSearchParam>();
47  	private IBaseResource myProfileDef;
48  	private String myResourceProfile;
49  	private List<RuntimeSearchParam> mySearchParams;
50  	private final FhirVersionEnum myStructureVersion;
51  	private volatile RuntimeResourceDefinition myBaseDefinition;
52  
53  
54  
55  	public RuntimeResourceDefinition(FhirContext theContext, String theResourceName, Class<? extends IBaseResource> theClass, ResourceDef theResourceAnnotation, boolean theStandardType, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
56  		super(theResourceName, theClass, theStandardType, theContext, theClassToElementDefinitions);
57  		myContext = theContext;
58  		myResourceProfile = theResourceAnnotation.profile();
59  		myId = theResourceAnnotation.id();
60  
61  		IBaseResource instance;
62  		try {
63  			instance = theClass.newInstance();
64  		} catch (Exception e) {
65  			throw new ConfigurationException(myContext.getLocalizer().getMessage(getClass(), "nonInstantiableType", theClass.getName(), e.toString()), e);
66  		}
67  		myStructureVersion = instance.getStructureFhirVersionEnum();
68  		if (myStructureVersion != theContext.getVersion().getVersion()) {
69  			throw new ConfigurationException(myContext.getLocalizer().getMessage(getClass(), "typeWrongVersion", theContext.getVersion().getVersion(), theClass.getName(), myStructureVersion));
70  		}
71  
72  	}
73  
74  
75  	public void addSearchParam(RuntimeSearchParam theParam) {
76  		myNameToSearchParam.put(theParam.getName(), theParam);
77  	}
78  
79  	/**
80  	 * If this definition refers to a class which extends another resource definition type, this
81  	 * method will return the definition of the topmost resource. For example, if this definition
82  	 * refers to MyPatient2, which extends MyPatient, which in turn extends Patient, this method
83  	 * will return the resource definition for Patient.
84  	 * <p>
85  	 * If the definition has no parent, returns <code>this</code>
86  	 * </p>
87  	 */
88  	public RuntimeResourceDefinition getBaseDefinition() {
89  		validateSealed();
90  		if (myBaseDefinition == null) {
91  			myBaseDefinition = myContext.getResourceDefinition(myBaseType);
92  		}
93  		return myBaseDefinition;
94  	}
95  
96  	@Override
97  	public ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum getChildType() {
98  		return ChildTypeEnum.RESOURCE;
99  	}
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 			String profileWithUrl = theServerBase + resourceName + profile;
135 			if (UrlUtil.isValid(profileWithUrl)) {
136 				return profileWithUrl;
137 			}
138 		}
139 		return profile;
140 	}
141 
142 	public RuntimeSearchParam getSearchParam(String theName) {
143 		validateSealed();
144 		return myNameToSearchParam.get(theName);
145 	}
146 
147 	public List<RuntimeSearchParam> getSearchParams() {
148 		validateSealed();
149 		return mySearchParams;
150 	}
151 
152 	/**
153 	 * Will not return null
154 	 */
155 	public List<RuntimeSearchParam> getSearchParamsForCompartmentName(String theCompartmentName) {
156 		validateSealed();
157 		List<RuntimeSearchParam> retVal = myCompartmentNameToSearchParams.get(theCompartmentName);
158 		if (retVal == null) {
159 			return Collections.emptyList();
160 		}
161 		return retVal;
162 	}
163 
164 	public FhirVersionEnum getStructureVersion() {
165 		return myStructureVersion;
166 	}
167 
168 	public boolean isBundle() {
169 		return "Bundle".equals(getName());
170 	}
171 
172 	@SuppressWarnings("unchecked")
173 	@Override
174 	public void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
175 		super.sealAndInitialize(theContext, theClassToElementDefinitions);
176 
177 		myNameToSearchParam = Collections.unmodifiableMap(myNameToSearchParam);
178 
179 		ArrayList<RuntimeSearchParam> searchParams = new ArrayList<RuntimeSearchParam>(myNameToSearchParam.values());
180 		Collections.sort(searchParams, new Comparator<RuntimeSearchParam>() {
181 			@Override
182 			public int compare(RuntimeSearchParam theArg0, RuntimeSearchParam theArg1) {
183 				return theArg0.getName().compareTo(theArg1.getName());
184 			}
185 		});
186 		mySearchParams = Collections.unmodifiableList(searchParams);
187 
188 		Map<String, List<RuntimeSearchParam>> compartmentNameToSearchParams = new HashMap<String, List<RuntimeSearchParam>>();
189 		for (RuntimeSearchParam next : searchParams) {
190 			if (next.getProvidesMembershipInCompartments() != null) {
191 				for (String nextCompartment : next.getProvidesMembershipInCompartments()) {
192 					if (!compartmentNameToSearchParams.containsKey(nextCompartment)) {
193 						compartmentNameToSearchParams.put(nextCompartment, new ArrayList<RuntimeSearchParam>());
194 					}
195 					compartmentNameToSearchParams.get(nextCompartment).add(next);
196 				}
197 			}
198 		}
199 		myCompartmentNameToSearchParams = Collections.unmodifiableMap(compartmentNameToSearchParams);
200 
201 		Class<?> target = getImplementingClass();
202 		myBaseType = (Class<? extends IBaseResource>) target;
203 		do {
204 			target = target.getSuperclass();
205 			if (IBaseResource.class.isAssignableFrom(target) && target.getAnnotation(ResourceDef.class) != null) {
206 				myBaseType = (Class<? extends IBaseResource>) target;
207 			}
208 		} while (target.equals(Object.class) == false);
209 		
210 		/*
211 		 * See #504:
212 		 * Bundle types may not have extensions
213 		 */
214 		if (hasExtensions()) {
215 			if (IAnyResource.class.isAssignableFrom(getImplementingClass())) {
216 				if (!IDomainResource.class.isAssignableFrom(getImplementingClass())) {
217 					throw new ConfigurationException("Class \"" + getImplementingClass() + "\" is invalid. This resource type is not a DomainResource, it must not have extensions");
218 				}
219 			}
220 		}
221 
222 	}
223 
224 	@Deprecated
225 	public synchronized IBaseResource toProfile() {
226 		validateSealed();
227 		if (myProfileDef != null) {
228 			return myProfileDef;
229 		}
230 
231 		IBaseResource retVal = myContext.getVersion().generateProfile(this, null);
232 		myProfileDef = retVal;
233 
234 		return retVal;
235 	}
236 
237 	public synchronized IBaseResource toProfile(String theServerBase) {
238 		validateSealed();
239 		if (myProfileDef != null) {
240 			return myProfileDef;
241 		}
242 
243 		IBaseResource retVal = myContext.getVersion().generateProfile(this, theServerBase);
244 		myProfileDef = retVal;
245 
246 		return retVal;
247 	}
248 
249 }