001package ca.uhn.fhir.util;
002
003import static org.apache.commons.lang3.StringUtils.defaultString;
004
005/*
006 * #%L
007 * HAPI FHIR - Core Library
008 * %%
009 * Copyright (C) 2014 - 2017 University Health Network
010 * %%
011 * Licensed under the Apache License, Version 2.0 (the "License");
012 * you may not use this file except in compliance with the License.
013 * You may obtain a copy of the License at
014 * 
015 *      http://www.apache.org/licenses/LICENSE-2.0
016 * 
017 * Unless required by applicable law or agreed to in writing, software
018 * distributed under the License is distributed on an "AS IS" BASIS,
019 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020 * See the License for the specific language governing permissions and
021 * limitations under the License.
022 * #L%
023 */
024import java.util.*;
025
026import org.apache.commons.lang3.Validate;
027import org.hl7.fhir.instance.model.api.*;
028
029import ca.uhn.fhir.context.*;
030import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
031import ca.uhn.fhir.model.api.ExtensionDt;
032import ca.uhn.fhir.model.api.IResource;
033import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
034import ca.uhn.fhir.model.base.composite.BaseContainedDt;
035import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
036import ca.uhn.fhir.model.primitive.StringDt;
037import ca.uhn.fhir.parser.DataFormatException;
038
039public class FhirTerser {
040
041        private FhirContext myContext;
042
043        public FhirTerser(FhirContext theContext) {
044                super();
045                myContext = theContext;
046        }
047
048        private List<String> addNameToList(List<String> theCurrentList, BaseRuntimeChildDefinition theChildDefinition) {
049                if (theChildDefinition == null)
050                        return null;
051                if (theCurrentList == null || theCurrentList.isEmpty())
052                        return new ArrayList<String>(Arrays.asList(theChildDefinition.getElementName()));
053                List<String> newList = new ArrayList<String>(theCurrentList);
054                newList.add(theChildDefinition.getElementName());
055                return newList;
056        }
057        
058
059        /**
060         * Clones all values from a source object into the equivalent fields in a target object
061         * @param theSource The source object (must not be null)
062         * @param theTarget The target object to copy values into (must not be null)
063         * @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)
064         */
065        public void cloneInto(IBase theSource, IBase theTarget, boolean theIgnoreMissingFields) {
066                Validate.notNull(theSource, "theSource must not be null");
067                Validate.notNull(theTarget, "theTarget must not be null");
068                
069                if (theSource instanceof IPrimitiveType<?>) {
070                        if (theTarget instanceof IPrimitiveType<?>) {
071                                ((IPrimitiveType<?>)theTarget).setValueAsString(((IPrimitiveType<?>)theSource).getValueAsString());
072                                return;
073                        }
074                        if (theIgnoreMissingFields) {
075                                return;
076                        }
077                        throw new DataFormatException("Can not copy value from primitive of type " + theSource.getClass().getName() + " into type " + theTarget.getClass().getName());
078                }
079                
080                BaseRuntimeElementCompositeDefinition<?> sourceDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theSource.getClass()); 
081                BaseRuntimeElementCompositeDefinition<?> targetDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theTarget.getClass());
082                
083                List<BaseRuntimeChildDefinition> children = sourceDef.getChildren();
084                if (sourceDef instanceof RuntimeExtensionDtDefinition) {
085                        children = ((RuntimeExtensionDtDefinition)sourceDef).getChildrenIncludingUrl();
086                }
087                
088                for (BaseRuntimeChildDefinition nextChild : children) {
089                        for (IBase nextValue : nextChild.getAccessor().getValues(theSource)) {
090                                String elementName = nextChild.getChildNameByDatatype(nextValue.getClass());
091                                BaseRuntimeChildDefinition targetChild = targetDef.getChildByName(elementName);
092                                if (targetChild == null) {
093                                        if (theIgnoreMissingFields) {
094                                                continue;
095                                        }
096                                        throw new DataFormatException("Type " + theTarget.getClass().getName() + " does not have a child with name " + elementName);
097                                }
098                                
099                                BaseRuntimeElementDefinition<?> childDef = targetChild.getChildByName(elementName);
100                                IBase target = childDef.newInstance();
101                                targetChild.getMutator().addValue(theTarget, target);
102                                cloneInto(nextValue, target, theIgnoreMissingFields);
103                        }
104                }
105                
106        }
107
108        /**
109         * 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.
110         * <p>
111         * 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
112         * well as any contained resources.
113         * </p>
114         * <p>
115         * 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.
116         * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
117         * </p>
118         * 
119         * @param theResource
120         *           The resource instance to search. Must not be null.
121         * @param theType
122         *           The type to search for. Must not be null.
123         * @return Returns a list of all matching elements
124         */
125        public <T extends IBase> List<T> getAllPopulatedChildElementsOfType(IBaseResource theResource, final Class<T> theType) {
126                final ArrayList<T> retVal = new ArrayList<T>();
127                BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
128                visit(new IdentityHashMap<Object, Object>(), theResource, theResource, null, null, def, new IModelVisitor() {
129                        @SuppressWarnings("unchecked")
130                        @Override
131                        public void acceptElement(IBaseResource theOuterResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
132                                if (theElement == null || theElement.isEmpty()) {
133                                        return;
134                                }
135
136                                if (theType.isAssignableFrom(theElement.getClass())) {
137                                        retVal.add((T) theElement);
138                                }
139                        }
140                });
141                return retVal;
142        }
143
144        public List<ResourceReferenceInfo> getAllResourceReferences(final IBaseResource theResource) {
145                final ArrayList<ResourceReferenceInfo> retVal = new ArrayList<ResourceReferenceInfo>();
146                BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
147                visit(new IdentityHashMap<Object, Object>(), theResource, theResource, null, null, def, new IModelVisitor() {
148                        @Override
149                        public void acceptElement(IBaseResource theOuterResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
150                                if (theElement == null || theElement.isEmpty()) {
151                                        return;
152                                }
153                                if (IBaseReference.class.isAssignableFrom(theElement.getClass())) {
154                                        retVal.add(new ResourceReferenceInfo(myContext, theOuterResource, thePathToElement, (IBaseReference) theElement));
155                                }
156                        }
157                });
158                return retVal;
159        }
160
161        private BaseRuntimeChildDefinition getDefinition(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, List<String> theSubList) {
162                BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(theSubList.get(0));
163
164                if (theSubList.size() == 1) {
165                        return nextDef;
166                }
167                BaseRuntimeElementCompositeDefinition<?> cmp = (BaseRuntimeElementCompositeDefinition<?>) nextDef.getChildByName(theSubList.get(0));
168                return getDefinition(cmp, theSubList.subList(1, theSubList.size()));
169        }
170
171        public BaseRuntimeChildDefinition getDefinition(Class<? extends IBaseResource> theResourceType, String thePath) {
172                RuntimeResourceDefinition def = myContext.getResourceDefinition(theResourceType);
173
174                BaseRuntimeElementCompositeDefinition<?> currentDef = def;
175
176                List<String> parts = Arrays.asList(thePath.split("\\."));
177                List<String> subList = parts.subList(1, parts.size());
178                if (subList.size() < 1) {
179                        throw new ConfigurationException("Invalid path: " + thePath);
180                }
181                return getDefinition(currentDef, subList);
182
183        }
184
185        public Object getSingleValueOrNull(IBase theTarget, String thePath) {
186                Class<Object> wantedType = Object.class;
187
188                return getSingleValueOrNull(theTarget, thePath, wantedType);
189        }
190
191        public <T> T getSingleValueOrNull(IBase theTarget, String thePath, Class<T> theWantedType) {
192                Validate.notNull(theTarget, "theTarget must not be null");
193                Validate.notBlank(thePath, "thePath must not be empty");
194
195                BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theTarget.getClass());
196                if (!(def instanceof BaseRuntimeElementCompositeDefinition)) {
197                        throw new IllegalArgumentException("Target is not a composite type: " + theTarget.getClass().getName());
198                }
199
200                BaseRuntimeElementCompositeDefinition<?> currentDef = (BaseRuntimeElementCompositeDefinition<?>) def;
201                Object currentObj = theTarget;
202
203                List<String> parts = parsePath(currentDef, thePath);
204
205                List<T> retVal = getValues(currentDef, currentObj, parts, theWantedType);
206                if (retVal.isEmpty()) {
207                        return null;
208                }
209                return retVal.get(0);
210        }
211
212        @SuppressWarnings("unchecked")
213        private <T> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, Object theCurrentObj, List<String> theSubList, Class<T> theWantedClass) {
214                String name = theSubList.get(0);
215                                
216                BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(name);
217                List<? extends IBase> values = nextDef.getAccessor().getValues(theCurrentObj);
218                List<T> retVal = new ArrayList<T>();
219
220                if (theSubList.size() == 1) {
221                        if (nextDef instanceof RuntimeChildChoiceDefinition) {
222                                for (IBase next : values) {
223                                        if (next != null) {
224                                                if (name.endsWith("[x]")) {
225                                                        if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) {
226                                                                retVal.add((T) next);
227                                                        }
228                                                } else {
229                                                        String childName = nextDef.getChildNameByDatatype(next.getClass());
230                                                        if (theSubList.get(0).equals(childName)) {
231                                                                if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) {
232                                                                        retVal.add((T) next);
233                                                                }
234                                                        }
235                                                }
236                                        }
237                                }
238                        } else {
239                                for (IBase next : values) {
240                                        if (next != null) {
241                                                if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) {
242                                                        retVal.add((T) next);
243                                                }
244                                        }
245                                }
246                        }
247                } else {
248                        for (IBase nextElement : values) {
249                                BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextElement.getClass());
250                                List<T> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()), theWantedClass);
251                                retVal.addAll(foundValues);
252                        }
253                }
254                return retVal;
255        }
256
257        public List<Object> getValues(IBaseResource theResource, String thePath) {
258                Class<Object> wantedClass = Object.class;
259
260                return getValues(theResource, thePath, wantedClass);
261
262        }
263
264        public <T> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass) {
265                RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
266                List<String> parts = parsePath(def, thePath);
267                return getValues(def, theResource, parts, theWantedClass);
268        }
269
270        private List<String> parsePath(BaseRuntimeElementCompositeDefinition<?> theElementDef, String thePath) {
271                List<String> parts = Arrays.asList(thePath.split("\\."));
272
273                if (theElementDef instanceof RuntimeResourceDefinition) {
274                        if (parts.size() > 0 && parts.get(0).equals(theElementDef.getName())) {
275                                parts = parts.subList(1, parts.size());
276                        }
277                }
278
279                if (parts.size() < 1) {
280                        throw new ConfigurationException("Invalid path: " + thePath);
281                }
282                return parts;
283        }
284
285        /**
286         * Returns <code>true</code> if <code>theSource</code> is in the compartment named <code>theCompartmentName</code>
287         * belonging to resource <code>theTarget</code>
288         * 
289         * @param theCompartmentName The name of the compartment
290         * @param theSource The potential member of the compartment
291         * @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}
292         * @return <code>true</code> if <code>theSource</code> is in the compartment
293         * @throws IllegalArgumentException If theTarget does not contain both a resource type and ID
294         */
295        public boolean isSourceInCompartmentForTarget(String theCompartmentName, IBaseResource theSource, IIdType theTarget) {
296                Validate.notBlank(theCompartmentName, "theCompartmentName must not be null or blank");
297                Validate.notNull(theSource, "theSource must not be null");
298                Validate.notNull(theTarget, "theTarget must not be null");
299                Validate.notBlank(defaultString(theTarget.getResourceType()), "theTarget must have a populated resource type (theTarget.getResourceType() does not return a value)");
300                Validate.notBlank(defaultString(theTarget.getIdPart()), "theTarget must have a populated ID (theTarget.getIdPart() does not return a value)");
301                
302                String wantRef = theTarget.toUnqualifiedVersionless().getValue();
303                
304                RuntimeResourceDefinition sourceDef = myContext.getResourceDefinition(theSource);
305                if (theSource.getIdElement().hasIdPart()) {
306                        if (wantRef.equals(sourceDef.getName() + '/' + theSource.getIdElement().getIdPart())) {
307                                return true;
308                        }
309                }
310                
311                List<RuntimeSearchParam> params = sourceDef.getSearchParamsForCompartmentName(theCompartmentName);
312                for (RuntimeSearchParam nextParam : params) {
313                        for (String nextPath : nextParam.getPathsSplit()) {
314                                for (IBaseReference nextValue : getValues(theSource, nextPath, IBaseReference.class)) {
315                                        String nextRef = nextValue.getReferenceElement().toUnqualifiedVersionless().getValue();
316                                        if (wantRef.equals(nextRef)) {
317                                                return true;
318                                        }
319                                }
320                        }
321                }
322                
323                return false;
324        }
325
326        private void visit(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor2 theCallback, List<IBase> theContainingElementPath,
327                        List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) {
328                if (theChildDefinition != null) {
329                        theChildDefinitionPath.add(theChildDefinition);
330                }
331                theContainingElementPath.add(theElement);
332                theElementDefinitionPath.add(theDefinition);
333
334                theCallback.acceptElement(theElement, Collections.unmodifiableList(theContainingElementPath), Collections.unmodifiableList(theChildDefinitionPath),
335                                Collections.unmodifiableList(theElementDefinitionPath));
336
337                /*
338                 * Visit undeclared extensions
339                 */
340                if (theElement instanceof ISupportsUndeclaredExtensions) {
341                        ISupportsUndeclaredExtensions containingElement = (ISupportsUndeclaredExtensions) theElement;
342                        for (ExtensionDt nextExt : containingElement.getUndeclaredExtensions()) {
343                                theContainingElementPath.add(nextExt);
344                                theCallback.acceptUndeclaredExtension(nextExt, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
345                                theContainingElementPath.remove(theContainingElementPath.size() - 1);
346                        }
347                }
348
349                /*
350                 * Now visit the children of the given element
351                 */
352                switch (theDefinition.getChildType()) {
353                case ID_DATATYPE:
354                case PRIMITIVE_XHTML_HL7ORG:
355                case PRIMITIVE_XHTML:
356                case PRIMITIVE_DATATYPE:
357                        // These are primitive types, so we don't need to visit their children
358                        break;
359                case RESOURCE:
360                case RESOURCE_BLOCK:
361                case COMPOSITE_DATATYPE: {
362                        BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) theDefinition;
363                        for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
364                                List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
365                                if (values != null) {
366                                        for (IBase nextValue : values) {
367                                                if (nextValue == null) {
368                                                        continue;
369                                                }
370                                                if (nextValue.isEmpty()) {
371                                                        continue;
372                                                }
373                                                BaseRuntimeElementDefinition<?> childElementDef;
374                                                childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
375
376                                                if (childElementDef == null) {
377                                                        StringBuilder b = new StringBuilder();
378                                                        b.append("Found value of type[");
379                                                        b.append(nextValue.getClass().getSimpleName());
380                                                        b.append("] which is not valid for field[");
381                                                        b.append(nextChild.getElementName());
382                                                        b.append("] in ");
383                                                        b.append(childDef.getName());
384                                                        b.append(" - Valid types: ");
385                                                        for (Iterator<String> iter = new TreeSet<String>(nextChild.getValidChildNames()).iterator(); iter.hasNext();) {
386                                                                BaseRuntimeElementDefinition<?> childByName = nextChild.getChildByName(iter.next());
387                                                                b.append(childByName.getImplementingClass().getSimpleName());
388                                                                if (iter.hasNext()) {
389                                                                        b.append(", ");
390                                                                }
391                                                        }
392                                                        throw new DataFormatException(b.toString());
393                                                }
394
395                                                if (nextChild instanceof RuntimeChildDirectResource) {
396                                                        // Don't descend into embedded resources
397                                                        theContainingElementPath.add(nextValue);
398                                                        theChildDefinitionPath.add(nextChild);
399                                                        theElementDefinitionPath.add(myContext.getElementDefinition(nextValue.getClass()));
400                                                        theCallback.acceptElement(nextValue, Collections.unmodifiableList(theContainingElementPath), Collections.unmodifiableList(theChildDefinitionPath),
401                                                                        Collections.unmodifiableList(theElementDefinitionPath));
402                                                        theChildDefinitionPath.remove(theChildDefinitionPath.size() - 1);
403                                                        theContainingElementPath.remove(theContainingElementPath.size() - 1);
404                                                        theElementDefinitionPath.remove(theElementDefinitionPath.size() - 1);
405                                                } else {
406                                                        visit(nextValue, nextChild, childElementDef, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
407                                                }
408                                        }
409                                }
410                        }
411                        break;
412                }
413                case CONTAINED_RESOURCES: {
414                        BaseContainedDt value = (BaseContainedDt) theElement;
415                        for (IResource next : value.getContainedResources()) {
416                                BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(next);
417                                visit(next, null, def, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
418                        }
419                        break;
420                }
421                case EXTENSION_DECLARED:
422                case UNDECL_EXT: {
423                        throw new IllegalStateException("state should not happen: " + theDefinition.getChildType());
424                }
425                case CONTAINED_RESOURCE_LIST: {
426                        if (theElement != null) {
427                                BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theElement.getClass());
428                                visit(theElement, null, def, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
429                        }
430                        break;
431                }
432                }
433
434                if (theChildDefinition != null) {
435                        theChildDefinitionPath.remove(theChildDefinitionPath.size() - 1);
436                }
437                theContainingElementPath.remove(theContainingElementPath.size() - 1);
438                theElementDefinitionPath.remove(theElementDefinitionPath.size() - 1);
439        }
440
441        /**
442         * Visit all elements in a given resource
443         * 
444         * <p>
445         * 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.
446         * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
447         * </p>
448         * 
449         * @param theResource
450         *           The resource to visit
451         * @param theVisitor
452         *           The visitor
453         */
454        public void visit(IBaseResource theResource, IModelVisitor theVisitor) {
455                BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
456                visit(new IdentityHashMap<Object, Object>(), theResource, theResource, null, null, def, theVisitor);
457        }
458
459        /**
460         * Visit all elements in a given resource
461         * 
462         * THIS ALTERNATE METHOD IS STILL EXPERIMENTAL
463         * 
464         * <p>
465         * 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.
466         * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
467         * </p>
468         * 
469         * @param theResource
470         *           The resource to visit
471         * @param theVisitor
472         *           The visitor
473         */
474        void visit(IBaseResource theResource, IModelVisitor2 theVisitor) {
475                BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
476                visit(theResource, null, def, theVisitor, new ArrayList<IBase>(), new ArrayList<BaseRuntimeChildDefinition>(), new ArrayList<BaseRuntimeElementDefinition<?>>());
477        }
478
479        private void visit(IdentityHashMap<Object, Object> theStack, IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition,
480                        BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor theCallback) {
481                List<String> pathToElement = addNameToList(thePathToElement, theChildDefinition);
482
483                if (theStack.put(theElement, theElement) != null) {
484                        return;
485                }
486                
487                theCallback.acceptElement(theResource, theElement, pathToElement, theChildDefinition, theDefinition);
488
489                BaseRuntimeElementDefinition<?> def = theDefinition;
490                if (def.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) {
491                        def = myContext.getElementDefinition(theElement.getClass());
492                }
493                
494                if (theElement instanceof IBaseReference) {
495                        IBaseResource target = ((IBaseReference)theElement).getResource();
496                        if (target != null) {
497                                if (target.getIdElement().hasIdPart() == false || target.getIdElement().isLocal()) {
498                                        RuntimeResourceDefinition targetDef = myContext.getResourceDefinition(target);
499                                        visit(theStack, target, target, pathToElement, null, targetDef, theCallback);
500                                }
501                        }
502                }
503                
504                switch (def.getChildType()) {
505                case ID_DATATYPE:
506                case PRIMITIVE_XHTML_HL7ORG:
507                case PRIMITIVE_XHTML:
508                case PRIMITIVE_DATATYPE:
509                        // These are primitive types
510                        break;
511                case RESOURCE:
512                case RESOURCE_BLOCK:
513                case COMPOSITE_DATATYPE: {
514                        BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def;
515                        for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
516                                
517                                List<?> values = nextChild.getAccessor().getValues(theElement);
518                                if (values != null) {
519                                        for (Object nextValueObject : values) {
520                                                IBase nextValue;
521                                                try {
522                                                        nextValue = (IBase) nextValueObject;
523                                                } catch (ClassCastException e) {
524                                                        String s = "Found instance of " + nextValueObject.getClass() + " - Did you set a field value to the incorrect type? Expected " + IBase.class.getName();
525                                                        throw new ClassCastException(s);
526                                                }
527                                                if (nextValue == null) {
528                                                        continue;
529                                                }
530                                                if (nextValue.isEmpty()) {
531                                                        continue;
532                                                }
533                                                BaseRuntimeElementDefinition<?> childElementDef;
534                                                childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
535
536                                                if (childElementDef == null) {
537                                                        childElementDef = myContext.getElementDefinition(nextValue.getClass());
538                                                }
539
540                                                if (nextChild instanceof RuntimeChildDirectResource) {
541                                                        // Don't descend into embedded resources
542                                                        theCallback.acceptElement(theResource, nextValue, null, nextChild, childElementDef);
543                                                } else {
544                                                        visit(theStack, theResource, nextValue, pathToElement, nextChild, childElementDef, theCallback);
545                                                }
546                                        }
547                                }
548                        }
549                        break;
550                }
551                case CONTAINED_RESOURCES: {
552                        BaseContainedDt value = (BaseContainedDt) theElement;
553                        for (IResource next : value.getContainedResources()) {
554                                def = myContext.getResourceDefinition(next);
555                                visit(theStack, next, next, pathToElement, null, def, theCallback);
556                        }
557                        break;
558                }
559                case CONTAINED_RESOURCE_LIST:
560                case EXTENSION_DECLARED:
561                case UNDECL_EXT: {
562                        throw new IllegalStateException("state should not happen: " + def.getChildType());
563                }
564                }
565                
566                theStack.remove(theElement);
567                
568        }
569
570}