View Javadoc
1   package ca.uhn.fhir.util;
2   
3   import static org.apache.commons.lang3.StringUtils.defaultString;
4   import static org.apache.commons.lang3.StringUtils.isBlank;
5   
6   /*
7    * #%L
8    * HAPI FHIR - Core Library
9    * %%
10   * Copyright (C) 2014 - 2018 University Health Network
11   * %%
12   * Licensed under the Apache License, Version 2.0 (the "License");
13   * you may not use this file except in compliance with the License.
14   * You may obtain a copy of the License at
15   * 
16   *      http://www.apache.org/licenses/LICENSE-2.0
17   * 
18   * Unless required by applicable law or agreed to in writing, software
19   * distributed under the License is distributed on an "AS IS" BASIS,
20   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21   * See the License for the specific language governing permissions and
22   * limitations under the License.
23   * #L%
24   */
25  import java.util.*;
26  import java.util.function.Predicate;
27  
28  import org.apache.commons.lang3.Validate;
29  import org.hl7.fhir.instance.model.api.*;
30  
31  import ca.uhn.fhir.context.*;
32  import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
33  import ca.uhn.fhir.model.api.ExtensionDt;
34  import ca.uhn.fhir.model.api.IResource;
35  import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
36  import ca.uhn.fhir.model.base.composite.BaseContainedDt;
37  import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
38  import ca.uhn.fhir.model.primitive.StringDt;
39  import ca.uhn.fhir.parser.DataFormatException;
40  
41  public class FhirTerser {
42  
43  	private FhirContext myContext;
44  
45  	public FhirTerser(FhirContext theContext) {
46  		super();
47  		myContext = theContext;
48  	}
49  
50  	private List<String> addNameToList(List<String> theCurrentList, BaseRuntimeChildDefinition theChildDefinition) {
51  		if (theChildDefinition == null)
52  			return null;
53  		if (theCurrentList == null || theCurrentList.isEmpty())
54  			return new ArrayList<String>(Arrays.asList(theChildDefinition.getElementName()));
55  		List<String> newList = new ArrayList<String>(theCurrentList);
56  		newList.add(theChildDefinition.getElementName());
57  		return newList;
58  	}
59  	
60  
61  	/**
62  	 * Clones all values from a source object into the equivalent fields in a target object
63  	 * @param theSource The source object (must not be null)
64  	 * @param theTarget The target object to copy values into (must not be null)
65  	 * @param theIgnoreMissingFields The ignore fields in the target which do not exist (if false, an exception will be thrown if the target is unable to accept a value from the source)
66  	 */
67  	public void cloneInto(IBase theSource, IBase theTarget, boolean theIgnoreMissingFields) {
68  		Validate.notNull(theSource, "theSource must not be null");
69  		Validate.notNull(theTarget, "theTarget must not be null");
70  		
71  		if (theSource instanceof IPrimitiveType<?>) {
72  			if (theTarget instanceof IPrimitiveType<?>) {
73  				((IPrimitiveType<?>)theTarget).setValueAsString(((IPrimitiveType<?>)theSource).getValueAsString());
74  				return;
75  			}
76  			if (theIgnoreMissingFields) {
77  				return;
78  			}
79  			throw new DataFormatException("Can not copy value from primitive of type " + theSource.getClass().getName() + " into type " + theTarget.getClass().getName());
80  		}
81  		
82  		BaseRuntimeElementCompositeDefinition<?> sourceDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theSource.getClass()); 
83  		BaseRuntimeElementCompositeDefinition<?> targetDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theTarget.getClass());
84  		
85  		List<BaseRuntimeChildDefinition> children = sourceDef.getChildren();
86  		if (sourceDef instanceof RuntimeExtensionDtDefinition) {
87  			children = ((RuntimeExtensionDtDefinition)sourceDef).getChildrenIncludingUrl();
88  		}
89  		
90  		for (BaseRuntimeChildDefinition nextChild : children) {
91  			for (IBase nextValue : nextChild.getAccessor().getValues(theSource)) {
92  				String elementName = nextChild.getChildNameByDatatype(nextValue.getClass());
93  				BaseRuntimeChildDefinition targetChild = targetDef.getChildByName(elementName);
94  				if (targetChild == null) {
95  					if (theIgnoreMissingFields) {
96  						continue;
97  					}
98  					throw new DataFormatException("Type " + theTarget.getClass().getName() + " does not have a child with name " + elementName);
99  				}
100 				
101 				BaseRuntimeElementDefinition<?> childDef = targetChild.getChildByName(elementName);
102 				IBase target = childDef.newInstance();
103 				targetChild.getMutator().addValue(theTarget, target);
104 				cloneInto(nextValue, target, theIgnoreMissingFields);
105 			}
106 		}
107 		
108 	}
109 
110 	/**
111 	 * Returns a list containing all child elements (including the resource itself) which are <b>non-empty</b> and are either of the exact type specified, or are a subclass of that type.
112 	 * <p>
113 	 * For example, specifying a type of {@link StringDt} would return all non-empty string instances within the message. Specifying a type of {@link IResource} would return the resource itself, as
114 	 * well as any contained resources.
115 	 * </p>
116 	 * <p>
117 	 * Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g.
118 	 * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
119 	 * </p>
120 	 * 
121 	 * @param theResource
122 	 *           The resource instance to search. Must not be null.
123 	 * @param theType
124 	 *           The type to search for. Must not be null.
125 	 * @return Returns a list of all matching elements
126 	 */
127 	public <T extends IBase> List<T> getAllPopulatedChildElementsOfType(IBaseResource theResource, final Class<T> theType) {
128 		final ArrayList<T> retVal = new ArrayList<T>();
129 		BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
130 		visit(new IdentityHashMap<Object, Object>(), theResource, theResource, null, null, def, new IModelVisitor() {
131 			@SuppressWarnings("unchecked")
132 			@Override
133 			public void acceptElement(IBaseResource theOuterResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
134 				if (theElement == null || theElement.isEmpty()) {
135 					return;
136 				}
137 
138 				if (theType.isAssignableFrom(theElement.getClass())) {
139 					retVal.add((T) theElement);
140 				}
141 			}
142 		});
143 		return retVal;
144 	}
145 
146 	public List<ResourceReferenceInfo> getAllResourceReferences(final IBaseResource theResource) {
147 		final ArrayList<ResourceReferenceInfo> retVal = new ArrayList<ResourceReferenceInfo>();
148 		BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
149 		visit(new IdentityHashMap<Object, Object>(), theResource, theResource, null, null, def, new IModelVisitor() {
150 			@Override
151 			public void acceptElement(IBaseResource theOuterResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
152 				if (theElement == null || theElement.isEmpty()) {
153 					return;
154 				}
155 				if (IBaseReference.class.isAssignableFrom(theElement.getClass())) {
156 					retVal.add(new ResourceReferenceInfo(myContext, theOuterResource, thePathToElement, (IBaseReference) theElement));
157 				}
158 			}
159 		});
160 		return retVal;
161 	}
162 
163 	private BaseRuntimeChildDefinition getDefinition(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, List<String> theSubList) {
164 		BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(theSubList.get(0));
165 
166 		if (theSubList.size() == 1) {
167 			return nextDef;
168 		}
169 		BaseRuntimeElementCompositeDefinition<?> cmp = (BaseRuntimeElementCompositeDefinition<?>) nextDef.getChildByName(theSubList.get(0));
170 		return getDefinition(cmp, theSubList.subList(1, theSubList.size()));
171 	}
172 
173 	public BaseRuntimeChildDefinition getDefinition(Class<? extends IBaseResource> theResourceType, String thePath) {
174 		RuntimeResourceDefinition def = myContext.getResourceDefinition(theResourceType);
175 
176 		BaseRuntimeElementCompositeDefinition<?> currentDef = def;
177 
178 		List<String> parts = Arrays.asList(thePath.split("\\."));
179 		List<String> subList = parts.subList(1, parts.size());
180 		if (subList.size() < 1) {
181 			throw new ConfigurationException("Invalid path: " + thePath);
182 		}
183 		return getDefinition(currentDef, subList);
184 
185 	}
186 
187 	public Object getSingleValueOrNull(IBase theTarget, String thePath) {
188 		Class<Object> wantedType = Object.class;
189 
190 		return getSingleValueOrNull(theTarget, thePath, wantedType);
191 	}
192 
193 	public <T> T getSingleValueOrNull(IBase theTarget, String thePath, Class<T> theWantedType) {
194 		Validate.notNull(theTarget, "theTarget must not be null");
195 		Validate.notBlank(thePath, "thePath must not be empty");
196 
197 		BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theTarget.getClass());
198 		if (!(def instanceof BaseRuntimeElementCompositeDefinition)) {
199 			throw new IllegalArgumentException("Target is not a composite type: " + theTarget.getClass().getName());
200 		}
201 
202 		BaseRuntimeElementCompositeDefinition<?> currentDef = (BaseRuntimeElementCompositeDefinition<?>) def;
203 		Object currentObj = theTarget;
204 
205 		List<String> parts = parsePath(currentDef, thePath);
206 
207 		List<T> retVal = getValues(currentDef, currentObj, parts, theWantedType);
208 		if (retVal.isEmpty()) {
209 			return null;
210 		}
211 		return retVal.get(0);
212 	}
213 
214 	@SuppressWarnings("unchecked")
215 	private <T> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, Object theCurrentObj, List<String> theSubList, Class<T> theWantedClass) {
216 		String name = theSubList.get(0);
217 		List<T> retVal = new ArrayList<>();
218 
219 		if (name.startsWith("extension('")) {
220 			String extensionUrl = name.substring("extension('".length());
221 			int endIndex = extensionUrl.indexOf('\'');
222 			if (endIndex != -1) {
223 				extensionUrl = extensionUrl.substring(0, endIndex);
224 			}
225 
226 			List<ExtensionDt> extensions= Collections.emptyList();
227 			if (theCurrentObj instanceof ISupportsUndeclaredExtensions) {
228 				extensions = ((ISupportsUndeclaredExtensions) theCurrentObj).getUndeclaredExtensionsByUrl(extensionUrl);
229 			} else if (theCurrentObj instanceof IBaseExtension) {
230 				extensions = ((IBaseExtension)theCurrentObj).getExtension();
231 			}
232 
233 			for (ExtensionDt next : extensions) {
234 				if (theWantedClass.isAssignableFrom(next.getClass())) {
235 					retVal.add((T) next);
236 				}
237 			}
238 
239 			if (theSubList.size() > 1) {
240 				List<T> values = retVal;
241 				retVal = new ArrayList<>();
242 				for (T nextElement : values) {
243 					BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition((Class<? extends IBase>) nextElement.getClass());
244 					List<T> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()), theWantedClass);
245 					retVal.addAll(foundValues);
246 				}
247 			}
248 
249 			return retVal;
250 		}
251 
252 		BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(name);
253 		List<? extends IBase> values = nextDef.getAccessor().getValues(theCurrentObj);
254 
255 		if (theSubList.size() == 1) {
256 			if (nextDef instanceof RuntimeChildChoiceDefinition) {
257 				for (IBase next : values) {
258 					if (next != null) {
259 						if (name.endsWith("[x]")) {
260 							if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) {
261 								retVal.add((T) next);
262 							}
263 						} else {
264 							String childName = nextDef.getChildNameByDatatype(next.getClass());
265 							if (theSubList.get(0).equals(childName)) {
266 								if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) {
267 									retVal.add((T) next);
268 								}
269 							}
270 						}
271 					}
272 				}
273 			} else {
274 				for (IBase next : values) {
275 					if (next != null) {
276 						if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) {
277 							retVal.add((T) next);
278 						}
279 					}
280 				}
281 			}
282 		} else {
283 			for (IBase nextElement : values) {
284 				BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextElement.getClass());
285 				List<T> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()), theWantedClass);
286 				retVal.addAll(foundValues);
287 			}
288 		}
289 		return retVal;
290 	}
291 
292 	public List<Object> getValues(IBaseResource theResource, String thePath) {
293 		Class<Object> wantedClass = Object.class;
294 
295 		return getValues(theResource, thePath, wantedClass);
296 
297 	}
298 
299 	public <T> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass) {
300 		RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
301 		List<String> parts = parsePath(def, thePath);
302 		return getValues(def, theResource, parts, theWantedClass);
303 	}
304 
305 	private List<String> parsePath(BaseRuntimeElementCompositeDefinition<?> theElementDef, String thePath) {
306 		List<String> parts = new ArrayList<>();
307 
308 		int currentStart = 0;
309 		boolean inSingleQuote = false;
310 		for (int i = 0; i < thePath.length(); i++) {
311 			switch (thePath.charAt(i)) {
312 				case '\'':
313 					inSingleQuote = !inSingleQuote;
314 					break;
315 				case '.':
316 					if (!inSingleQuote) {
317 						parts.add(thePath.substring(currentStart, i));
318 						currentStart = i + 1;
319 					}
320 					break;
321 			}
322 		}
323 
324 		parts.add(thePath.substring(currentStart));
325 
326 		if (theElementDef instanceof RuntimeResourceDefinition) {
327 			if (parts.size() > 0 && parts.get(0).equals(theElementDef.getName())) {
328 				parts = parts.subList(1, parts.size());
329 			}
330 		}
331 
332 		if (parts.size() < 1) {
333 			throw new ConfigurationException("Invalid path: " + thePath);
334 		}
335 		return parts;
336 	}
337 
338 	/**
339 	 * Returns <code>true</code> if <code>theSource</code> is in the compartment named <code>theCompartmentName</code>
340 	 * belonging to resource <code>theTarget</code>
341 	 * 
342 	 * @param theCompartmentName The name of the compartment
343 	 * @param theSource The potential member of the compartment
344 	 * @param theTarget The owner of the compartment. Note that both the resource type and ID must be filled in on this IIdType or the method will throw an {@link IllegalArgumentException}
345 	 * @return <code>true</code> if <code>theSource</code> is in the compartment
346 	 * @throws IllegalArgumentException If theTarget does not contain both a resource type and ID
347 	 */
348 	public boolean isSourceInCompartmentForTarget(String theCompartmentName, IBaseResource theSource, IIdType theTarget) {
349 		Validate.notBlank(theCompartmentName, "theCompartmentName must not be null or blank");
350 		Validate.notNull(theSource, "theSource must not be null");
351 		Validate.notNull(theTarget, "theTarget must not be null");
352 		Validate.notBlank(defaultString(theTarget.getResourceType()), "theTarget must have a populated resource type (theTarget.getResourceType() does not return a value)");
353 		Validate.notBlank(defaultString(theTarget.getIdPart()), "theTarget must have a populated ID (theTarget.getIdPart() does not return a value)");
354 		
355 		String wantRef = theTarget.toUnqualifiedVersionless().getValue();
356 		
357 		RuntimeResourceDefinition sourceDef = myContext.getResourceDefinition(theSource);
358 		if (theSource.getIdElement().hasIdPart()) {
359 			if (wantRef.equals(sourceDef.getName() + '/' + theSource.getIdElement().getIdPart())) {
360 				return true;
361 			}
362 		}
363 		
364 		List<RuntimeSearchParam> params = sourceDef.getSearchParamsForCompartmentName(theCompartmentName);
365 		for (RuntimeSearchParam nextParam : params) {
366 			for (String nextPath : nextParam.getPathsSplit()) {
367 				for (IBaseReference nextValue : getValues(theSource, nextPath, IBaseReference.class)) {
368 					String nextRef = nextValue.getReferenceElement().toUnqualifiedVersionless().getValue();
369 
370 					/*
371 					 * If the reference isn't an explicit resource ID, but instead is just
372 					 * a resource object, we'll calculate its ID and treat the target
373 					 * as that.
374 					 */
375 					if (isBlank(nextRef) && nextValue.getResource() != null) {
376 						IBaseResource nextTarget = nextValue.getResource();
377 						IIdType nextTargetId = nextTarget.getIdElement().toUnqualifiedVersionless();
378 						if (!nextTargetId.hasResourceType()) {
379 							String resourceType = myContext.getResourceDefinition(nextTarget).getName();
380 							nextTargetId.setParts(null, resourceType, nextTargetId.getIdPart(), null);
381 						}
382 						nextRef = nextTargetId.getValue();
383 					}
384 
385 					if (wantRef.equals(nextRef)) {
386 						return true;
387 					}
388 				}
389 			}
390 		}
391 		
392 		return false;
393 	}
394 
395 	private void visit(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor2 theCallback, List<IBase> theContainingElementPath,
396 			List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) {
397 		if (theChildDefinition != null) {
398 			theChildDefinitionPath.add(theChildDefinition);
399 		}
400 		theContainingElementPath.add(theElement);
401 		theElementDefinitionPath.add(theDefinition);
402 
403 		theCallback.acceptElement(theElement, Collections.unmodifiableList(theContainingElementPath), Collections.unmodifiableList(theChildDefinitionPath),
404 				Collections.unmodifiableList(theElementDefinitionPath));
405 
406 		/*
407 		 * Visit undeclared extensions
408 		 */
409 		if (theElement instanceof ISupportsUndeclaredExtensions) {
410 			ISupportsUndeclaredExtensions containingElement = (ISupportsUndeclaredExtensions) theElement;
411 			for (ExtensionDt nextExt : containingElement.getUndeclaredExtensions()) {
412 				theContainingElementPath.add(nextExt);
413 				theCallback.acceptUndeclaredExtension(nextExt, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
414 				theContainingElementPath.remove(theContainingElementPath.size() - 1);
415 			}
416 		}
417 
418 		/*
419 		 * Now visit the children of the given element
420 		 */
421 		switch (theDefinition.getChildType()) {
422 		case ID_DATATYPE:
423 		case PRIMITIVE_XHTML_HL7ORG:
424 		case PRIMITIVE_XHTML:
425 		case PRIMITIVE_DATATYPE:
426 			// These are primitive types, so we don't need to visit their children
427 			break;
428 		case RESOURCE:
429 		case RESOURCE_BLOCK:
430 		case COMPOSITE_DATATYPE: {
431 			BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) theDefinition;
432 			for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
433 				List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
434 				if (values != null) {
435 					for (IBase nextValue : values) {
436 						if (nextValue == null) {
437 							continue;
438 						}
439 						if (nextValue.isEmpty()) {
440 							continue;
441 						}
442 						BaseRuntimeElementDefinition<?> childElementDef;
443 						childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
444 
445 						if (childElementDef == null) {
446 							StringBuilder b = new StringBuilder();
447 							b.append("Found value of type[");
448 							b.append(nextValue.getClass().getSimpleName());
449 							b.append("] which is not valid for field[");
450 							b.append(nextChild.getElementName());
451 							b.append("] in ");
452 							b.append(childDef.getName());
453 							b.append(" - Valid types: ");
454 							for (Iterator<String> iter = new TreeSet<String>(nextChild.getValidChildNames()).iterator(); iter.hasNext();) {
455 								BaseRuntimeElementDefinition<?> childByName = nextChild.getChildByName(iter.next());
456 								b.append(childByName.getImplementingClass().getSimpleName());
457 								if (iter.hasNext()) {
458 									b.append(", ");
459 								}
460 							}
461 							throw new DataFormatException(b.toString());
462 						}
463 
464 						if (nextChild instanceof RuntimeChildDirectResource) {
465 							// Don't descend into embedded resources
466 							theContainingElementPath.add(nextValue);
467 							theChildDefinitionPath.add(nextChild);
468 							theElementDefinitionPath.add(myContext.getElementDefinition(nextValue.getClass()));
469 							theCallback.acceptElement(nextValue, Collections.unmodifiableList(theContainingElementPath), Collections.unmodifiableList(theChildDefinitionPath),
470 									Collections.unmodifiableList(theElementDefinitionPath));
471 							theChildDefinitionPath.remove(theChildDefinitionPath.size() - 1);
472 							theContainingElementPath.remove(theContainingElementPath.size() - 1);
473 							theElementDefinitionPath.remove(theElementDefinitionPath.size() - 1);
474 						} else {
475 							visit(nextValue, nextChild, childElementDef, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
476 						}
477 					}
478 				}
479 			}
480 			break;
481 		}
482 		case CONTAINED_RESOURCES: {
483 			BaseContainedDt value = (BaseContainedDt) theElement;
484 			for (IResource next : value.getContainedResources()) {
485 				BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(next);
486 				visit(next, null, def, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
487 			}
488 			break;
489 		}
490 		case EXTENSION_DECLARED:
491 		case UNDECL_EXT: {
492 			throw new IllegalStateException("state should not happen: " + theDefinition.getChildType());
493 		}
494 		case CONTAINED_RESOURCE_LIST: {
495 			if (theElement != null) {
496 				BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theElement.getClass());
497 				visit(theElement, null, def, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
498 			}
499 			break;
500 		}
501 		}
502 
503 		if (theChildDefinition != null) {
504 			theChildDefinitionPath.remove(theChildDefinitionPath.size() - 1);
505 		}
506 		theContainingElementPath.remove(theContainingElementPath.size() - 1);
507 		theElementDefinitionPath.remove(theElementDefinitionPath.size() - 1);
508 	}
509 
510 	/**
511 	 * Visit all elements in a given resource
512 	 * 
513 	 * <p>
514 	 * Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g.
515 	 * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
516 	 * </p>
517 	 * 
518 	 * @param theResource
519 	 *           The resource to visit
520 	 * @param theVisitor
521 	 *           The visitor
522 	 */
523 	public void visit(IBaseResource theResource, IModelVisitor theVisitor) {
524 		BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
525 		visit(new IdentityHashMap<Object, Object>(), theResource, theResource, null, null, def, theVisitor);
526 	}
527 
528 	/**
529 	 * Visit all elements in a given resource
530 	 * 
531 	 * THIS ALTERNATE METHOD IS STILL EXPERIMENTAL
532 	 * 
533 	 * <p>
534 	 * Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g.
535 	 * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
536 	 * </p>
537 	 * 
538 	 * @param theResource
539 	 *           The resource to visit
540 	 * @param theVisitor
541 	 *           The visitor
542 	 */
543 	void visit(IBaseResource theResource, IModelVisitor2 theVisitor) {
544 		BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
545 		visit(theResource, null, def, theVisitor, new ArrayList<IBase>(), new ArrayList<BaseRuntimeChildDefinition>(), new ArrayList<BaseRuntimeElementDefinition<?>>());
546 	}
547 
548 	private void visit(IdentityHashMap<Object, Object> theStack, IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition,
549 			BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor theCallback) {
550 		List<String> pathToElement = addNameToList(thePathToElement, theChildDefinition);
551 
552 		if (theStack.put(theElement, theElement) != null) {
553 			return;
554 		}
555 		
556 		theCallback.acceptElement(theResource, theElement, pathToElement, theChildDefinition, theDefinition);
557 
558 		BaseRuntimeElementDefinition<?> def = theDefinition;
559 		if (def.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) {
560 			def = myContext.getElementDefinition(theElement.getClass());
561 		}
562 		
563 		if (theElement instanceof IBaseReference) {
564 			IBaseResource target = ((IBaseReference)theElement).getResource();
565 			if (target != null) {
566 				if (target.getIdElement().hasIdPart() == false || target.getIdElement().isLocal()) {
567 					RuntimeResourceDefinition targetDef = myContext.getResourceDefinition(target);
568 					visit(theStack, target, target, pathToElement, null, targetDef, theCallback);
569 				}
570 			}
571 		}
572 		
573 		switch (def.getChildType()) {
574 		case ID_DATATYPE:
575 		case PRIMITIVE_XHTML_HL7ORG:
576 		case PRIMITIVE_XHTML:
577 		case PRIMITIVE_DATATYPE:
578 			// These are primitive types
579 			break;
580 		case RESOURCE:
581 		case RESOURCE_BLOCK:
582 		case COMPOSITE_DATATYPE: {
583 			BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def;
584 			for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
585 				
586 				List<?> values = nextChild.getAccessor().getValues(theElement);
587 				if (values != null) {
588 					for (Object nextValueObject : values) {
589 						IBase nextValue;
590 						try {
591 							nextValue = (IBase) nextValueObject;
592 						} catch (ClassCastException e) {
593 							String s = "Found instance of " + nextValueObject.getClass() + " - Did you set a field value to the incorrect type? Expected " + IBase.class.getName();
594 							throw new ClassCastException(s);
595 						}
596 						if (nextValue == null) {
597 							continue;
598 						}
599 						if (nextValue.isEmpty()) {
600 							continue;
601 						}
602 						BaseRuntimeElementDefinition<?> childElementDef;
603 						childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
604 
605 						if (childElementDef == null) {
606 							childElementDef = myContext.getElementDefinition(nextValue.getClass());
607 						}
608 
609 						if (nextChild instanceof RuntimeChildDirectResource) {
610 							// Don't descend into embedded resources
611 							theCallback.acceptElement(theResource, nextValue, null, nextChild, childElementDef);
612 						} else {
613 							visit(theStack, theResource, nextValue, pathToElement, nextChild, childElementDef, theCallback);
614 						}
615 					}
616 				}
617 			}
618 			break;
619 		}
620 		case CONTAINED_RESOURCES: {
621 			BaseContainedDt value = (BaseContainedDt) theElement;
622 			for (IResource next : value.getContainedResources()) {
623 				def = myContext.getResourceDefinition(next);
624 				visit(theStack, next, next, pathToElement, null, def, theCallback);
625 			}
626 			break;
627 		}
628 		case CONTAINED_RESOURCE_LIST:
629 		case EXTENSION_DECLARED:
630 		case UNDECL_EXT: {
631 			throw new IllegalStateException("state should not happen: " + def.getChildType());
632 		}
633 		}
634 		
635 		theStack.remove(theElement);
636 		
637 	}
638 
639 }