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}