1 package ca.uhn.fhir.context;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import static org.apache.commons.lang3.StringUtils.*;
24
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.LinkedHashMap;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.apache.commons.lang3.StringUtils;
36 import org.apache.commons.lang3.text.WordUtils;
37
38 import ca.uhn.fhir.model.api.IElement;
39 import ca.uhn.fhir.model.api.IPrimitiveDatatype;
40 import ca.uhn.fhir.model.api.IResource;
41 import ca.uhn.fhir.model.api.annotation.Child;
42 import ca.uhn.fhir.model.api.annotation.ResourceDef;
43 import ca.uhn.fhir.model.dstu.resource.Profile;
44 import ca.uhn.fhir.model.dstu.resource.Profile.ExtensionDefn;
45 import ca.uhn.fhir.model.dstu.resource.Profile.Structure;
46 import ca.uhn.fhir.model.dstu.resource.Profile.StructureElement;
47 import ca.uhn.fhir.model.dstu.resource.Profile.StructureElementDefinitionType;
48 import ca.uhn.fhir.model.dstu.valueset.DataTypeEnum;
49 import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
50 import ca.uhn.fhir.model.primitive.IdDt;
51
52 public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefinition<IResource> {
53
54 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RuntimeResourceDefinition.class);
55 private RuntimeResourceDefinition myBaseDefinition;
56 private Map<RuntimeChildDeclaredExtensionDefinition, String> myExtensionDefToCode = new HashMap<RuntimeChildDeclaredExtensionDefinition, String>();
57 private String myId;
58 private Map<String, RuntimeSearchParam> myNameToSearchParam = new LinkedHashMap<String, RuntimeSearchParam>();
59 private Profile myProfileDef;
60 private String myResourceProfile;
61 private List<RuntimeSearchParam> mySearchParams;
62
63 public RuntimeResourceDefinition(String theResourceName, Class<? extends IResource> theClass, ResourceDef theResourceAnnotation) {
64 super(theResourceName, theClass);
65 myResourceProfile = theResourceAnnotation.profile();
66 myId = theResourceAnnotation.id();
67 }
68
69 public void addSearchParam(RuntimeSearchParam theParam) {
70 myNameToSearchParam.put(theParam.getName(), theParam);
71 }
72
73
74
75
76
77
78
79
80
81
82 public RuntimeResourceDefinition getBaseDefinition() {
83 return myBaseDefinition;
84 }
85
86 @Override
87 public ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum getChildType() {
88 return ChildTypeEnum.RESOURCE;
89 }
90
91 public String getResourceProfile() {
92 return myResourceProfile;
93 }
94
95 public RuntimeSearchParam getSearchParam(String theName) {
96 return myNameToSearchParam.get(theName);
97 }
98
99 public List<RuntimeSearchParam> getSearchParams() {
100 return mySearchParams;
101 }
102
103 public boolean isStandardProfile() {
104 return myResourceProfile.startsWith("http://hl7.org/fhir/profiles");
105 }
106
107 @Override
108 public void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
109 super.sealAndInitialize(theClassToElementDefinitions);
110
111 myNameToSearchParam = Collections.unmodifiableMap(myNameToSearchParam);
112
113 ArrayList<RuntimeSearchParam> searchParams = new ArrayList<RuntimeSearchParam>(myNameToSearchParam.values());
114 Collections.sort(searchParams, new Comparator<RuntimeSearchParam>() {
115 @Override
116 public int compare(RuntimeSearchParam theArg0, RuntimeSearchParam theArg1) {
117 return theArg0.getName().compareTo(theArg1.getName());
118 }
119 });
120 mySearchParams = Collections.unmodifiableList(searchParams);
121
122 Class<?> target = getImplementingClass();
123 myBaseDefinition = this;
124 do {
125 target = target.getSuperclass();
126 if (IResource.class.isAssignableFrom(target) && target.getAnnotation(ResourceDef.class)!=null) {
127 myBaseDefinition = (RuntimeResourceDefinition) theClassToElementDefinitions.get(target);
128 }
129 } while (target.equals(Object.class)==false);
130 }
131
132 public synchronized Profile toProfile() {
133 if (myProfileDef != null) {
134 return myProfileDef;
135 }
136
137 Profile retVal = new Profile();
138
139 RuntimeResourceDefinition def = this;
140
141 if (StringUtils.isBlank(myId)) {
142 myId = getName().toLowerCase();
143 }
144
145 retVal.setId(new IdDt(myId));
146
147
148 scanForExtensions(retVal, def);
149 Collections.sort(retVal.getExtensionDefn(), new Comparator<ExtensionDefn>() {
150 @Override
151 public int compare(ExtensionDefn theO1, ExtensionDefn theO2) {
152 return theO1.getCode().compareTo(theO2.getCode());
153 }
154 });
155
156
157 retVal.setName(getName());
158 Structure struct = retVal.addStructure();
159 LinkedList<String> path = new LinkedList<String>();
160
161 StructureElement element = struct.addElement();
162 element.getDefinition().setMin(1);
163 element.getDefinition().setMax("1");
164
165 fillProfile(struct, element, def, path, null);
166
167 retVal.getStructure().get(0).getElement().get(0).getDefinition().addType().getCode().setValue("Resource");
168
169 myProfileDef = retVal;
170
171 return retVal;
172 }
173
174 private void fillBasics(StructureElement theElement, BaseRuntimeElementDefinition<?> def, LinkedList<String> path, BaseRuntimeDeclaredChildDefinition theChild) {
175 if (path.isEmpty()) {
176 path.add(def.getName());
177 theElement.setName(def.getName());
178 } else {
179 path.add(WordUtils.uncapitalize(theChild.getElementName()));
180 theElement.setName(theChild.getElementName());
181 }
182 theElement.setPath(StringUtils.join(path, '.'));
183 }
184
185 private void fillExtensions(Structure theStruct, LinkedList<String> path, List<RuntimeChildDeclaredExtensionDefinition> extList, String elementName, boolean theIsModifier) {
186 if (extList.size() > 0) {
187 StructureElement extSlice = theStruct.addElement();
188 extSlice.setName(elementName);
189 extSlice.setPath(join(path, '.') + '.' + elementName);
190 extSlice.getSlicing().getDiscriminator().setValue("url");
191 extSlice.getSlicing().setOrdered(false);
192 extSlice.getSlicing().setRules(SlicingRulesEnum.OPEN);
193 extSlice.getDefinition().addType().setCode(DataTypeEnum.EXTENSION);
194
195 for (RuntimeChildDeclaredExtensionDefinition nextExt : extList) {
196 StructureElement nextProfileExt = theStruct.addElement();
197 nextProfileExt.getDefinition().setIsModifier(theIsModifier);
198 nextProfileExt.setName(extSlice.getName());
199 nextProfileExt.setPath(extSlice.getPath());
200 fillMinAndMaxAndDefinitions(nextExt, nextProfileExt);
201 StructureElementDefinitionType type = nextProfileExt.getDefinition().addType();
202 type.setCode(DataTypeEnum.EXTENSION);
203 if (nextExt.isDefinedLocally()) {
204 type.setProfile(nextExt.getExtensionUrl().substring(nextExt.getExtensionUrl().indexOf('#')));
205 } else {
206 type.setProfile(nextExt.getExtensionUrl());
207 }
208 }
209 } else {
210 StructureElement extSlice = theStruct.addElement();
211 extSlice.setName(elementName);
212 extSlice.setPath(join(path, '.') + '.' + elementName);
213 extSlice.getDefinition().setIsModifier(theIsModifier);
214 extSlice.getDefinition().addType().setCode(DataTypeEnum.EXTENSION);
215 extSlice.getDefinition().setMin(0);
216 extSlice.getDefinition().setMax("*");
217 }
218 }
219
220 private void fillMinAndMaxAndDefinitions(BaseRuntimeDeclaredChildDefinition child, StructureElement elem) {
221 elem.getDefinition().setMin(child.getMin());
222 if (child.getMax() == Child.MAX_UNLIMITED) {
223 elem.getDefinition().setMax("*");
224 } else {
225 elem.getDefinition().setMax(Integer.toString(child.getMax()));
226 }
227
228 if (isNotBlank(child.getShortDefinition())) {
229 elem.getDefinition().getShort().setValue(child.getShortDefinition());
230 }
231 if (isNotBlank(child.getFormalDefinition())) {
232 elem.getDefinition().getFormal().setValue(child.getFormalDefinition());
233 }
234 }
235
236 private void fillName(StructureElement elem, BaseRuntimeElementDefinition<?> nextDef) {
237 if (nextDef instanceof RuntimeResourceReferenceDefinition) {
238 RuntimeResourceReferenceDefinition rr = (RuntimeResourceReferenceDefinition) nextDef;
239 for (Class<? extends IResource> next : rr.getResourceTypes()) {
240 StructureElementDefinitionType type = elem.getDefinition().addType();
241 type.getCode().setValue("ResourceReference");
242
243 if (next != IResource.class) {
244 RuntimeResourceDefinition resDef = rr.getDefinitionForResourceType(next);
245 type.getProfile().setValueAsString(resDef.getResourceProfile());
246 }
247 }
248
249 return;
250 }
251
252 StructureElementDefinitionType type = elem.getDefinition().addType();
253 String name = nextDef.getName();
254 DataTypeEnum fromCodeString = DataTypeEnum.VALUESET_BINDER.fromCodeString(name);
255 if (fromCodeString == null) {
256 throw new ConfigurationException("Unknown type: " + name);
257 }
258 type.setCode(fromCodeString);
259 }
260
261 private void fillProfile(Structure theStruct, StructureElement theElement, BaseRuntimeElementDefinition<?> def, LinkedList<String> path, BaseRuntimeDeclaredChildDefinition theChild) {
262
263 fillBasics(theElement, def, path, theChild);
264
265 String expectedPath = StringUtils.join(path, '.');
266
267 ourLog.info("Filling profile for: {} - Path: {}", expectedPath);
268 String name = def.getName();
269 if (!expectedPath.equals(name)) {
270 path.pollLast();
271 theElement.getDefinition().getNameReference().setValue(def.getName());
272 return;
273 }
274
275 fillExtensions(theStruct, path, def.getExtensionsNonModifier(), "extension", false);
276 fillExtensions(theStruct, path, def.getExtensionsModifier(), "modifierExtension", true);
277
278 if (def.getChildType() == ChildTypeEnum.RESOURCE) {
279 StructureElement narrative = theStruct.addElement();
280 narrative.setName("text");
281 narrative.setPath(join(path, '.') + ".text");
282 narrative.getDefinition().addType().setCode(DataTypeEnum.NARRATIVE);
283 narrative.getDefinition().setIsModifier(false);
284 narrative.getDefinition().setMin(0);
285 narrative.getDefinition().setMax("1");
286
287 StructureElement contained = theStruct.addElement();
288 contained.setName("contained");
289 contained.setPath(join(path, '.') + ".contained");
290 contained.getDefinition().addType().getCode().setValue("Resource");
291 contained.getDefinition().setIsModifier(false);
292 contained.getDefinition().setMin(0);
293 contained.getDefinition().setMax("1");
294 }
295
296 if (def instanceof BaseRuntimeElementCompositeDefinition) {
297 BaseRuntimeElementCompositeDefinition<?> cdef = ((BaseRuntimeElementCompositeDefinition<?>) def);
298 for (BaseRuntimeChildDefinition nextChild : cdef.getChildren()) {
299 if (nextChild instanceof RuntimeChildUndeclaredExtensionDefinition) {
300 continue;
301 }
302
303 BaseRuntimeDeclaredChildDefinition child = (BaseRuntimeDeclaredChildDefinition) nextChild;
304 StructureElement elem = theStruct.addElement();
305 fillMinAndMaxAndDefinitions(child, elem);
306
307 if (child instanceof RuntimeChildResourceBlockDefinition) {
308 RuntimeResourceBlockDefinition nextDef = (RuntimeResourceBlockDefinition) child.getSingleChildOrThrow();
309 fillProfile(theStruct, elem, nextDef, path, child);
310 } else if (child instanceof RuntimeChildContainedResources) {
311
312 } else if (child instanceof RuntimeChildDeclaredExtensionDefinition) {
313 throw new IllegalStateException("Unexpected child type: " + child.getClass().getCanonicalName());
314 } else if (child instanceof RuntimeChildCompositeDatatypeDefinition || child instanceof RuntimeChildPrimitiveDatatypeDefinition || child instanceof RuntimeChildChoiceDefinition
315 || child instanceof RuntimeChildResourceDefinition) {
316 Iterator<String> childNamesIter = child.getValidChildNames().iterator();
317 String nextName = childNamesIter.next();
318 BaseRuntimeElementDefinition<?> nextDef = child.getChildByName(nextName);
319 fillBasics(elem, nextDef, path, child);
320 fillName(elem, nextDef);
321 while (childNamesIter.hasNext()) {
322 nextDef = child.getChildByName(childNamesIter.next());
323 fillName(elem, nextDef);
324 }
325 path.pollLast();
326 } else {
327 throw new IllegalStateException("Unexpected child type: " + child.getClass().getCanonicalName());
328 }
329
330 }
331 } else {
332 throw new IllegalStateException("Unexpected child type: " + def.getClass().getCanonicalName());
333 }
334
335 path.pollLast();
336 }
337
338 private void scanForExtensions(Profile theProfile, BaseRuntimeElementDefinition<?> def) {
339 BaseRuntimeElementCompositeDefinition<?> cdef = ((BaseRuntimeElementCompositeDefinition<?>) def);
340
341 for (RuntimeChildDeclaredExtensionDefinition nextChild : cdef.getExtensions()) {
342 if (myExtensionDefToCode.containsKey(nextChild)) {
343 continue;
344 }
345
346 if (nextChild.isDefinedLocally() == false) {
347 continue;
348 }
349
350 ExtensionDefn defn = theProfile.addExtensionDefn();
351 String code = null;
352 if (nextChild.getExtensionUrl().contains("#") && !nextChild.getExtensionUrl().endsWith("#")) {
353 code = nextChild.getExtensionUrl().substring(nextChild.getExtensionUrl().indexOf('#') + 1);
354 } else {
355 throw new ConfigurationException("Locally defined extension has no '#[code]' part in extension URL: " + nextChild.getExtensionUrl());
356 }
357
358 defn.setCode(code);
359 if (myExtensionDefToCode.values().contains(code)) {
360 throw new IllegalStateException("Duplicate extension code: " + code);
361 }
362 myExtensionDefToCode.put(nextChild, code);
363
364 if (nextChild.getChildType() != null && IPrimitiveDatatype.class.isAssignableFrom(nextChild.getChildType())) {
365 RuntimePrimitiveDatatypeDefinition pdef = (RuntimePrimitiveDatatypeDefinition) nextChild.getSingleChildOrThrow();
366 defn.getDefinition().addType().setCode(DataTypeEnum.VALUESET_BINDER.fromCodeString(pdef.getName()));
367 } else {
368 RuntimeResourceBlockDefinition pdef = (RuntimeResourceBlockDefinition) nextChild.getSingleChildOrThrow();
369 scanForExtensions(theProfile, pdef);
370
371 for (RuntimeChildDeclaredExtensionDefinition nextChildExt : pdef.getExtensions()) {
372 StructureElementDefinitionType type = defn.getDefinition().addType();
373 type.setCode(DataTypeEnum.EXTENSION);
374 type.setProfile("#" + myExtensionDefToCode.get(nextChildExt));
375 }
376
377 }
378 }
379 }
380
381 }