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 = Arrays.asList(thePath.split("\\.")); 204 List<T> retVal = getValues(currentDef, currentObj, parts, theWantedType); 205 if (retVal.isEmpty()) { 206 return null; 207 } 208 return retVal.get(0); 209 } 210 211 @SuppressWarnings("unchecked") 212 private <T> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, Object theCurrentObj, List<String> theSubList, Class<T> theWantedClass) { 213 String name = theSubList.get(0); 214 215 BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(name); 216 List<? extends IBase> values = nextDef.getAccessor().getValues(theCurrentObj); 217 List<T> retVal = new ArrayList<T>(); 218 219 if (theSubList.size() == 1) { 220 if (nextDef instanceof RuntimeChildChoiceDefinition) { 221 for (IBase next : values) { 222 if (next != null) { 223 if (name.endsWith("[x]")) { 224 if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) { 225 retVal.add((T) next); 226 } 227 } else { 228 String childName = nextDef.getChildNameByDatatype(next.getClass()); 229 if (theSubList.get(0).equals(childName)) { 230 if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) { 231 retVal.add((T) next); 232 } 233 } 234 } 235 } 236 } 237 } else { 238 for (IBase next : values) { 239 if (next != null) { 240 if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) { 241 retVal.add((T) next); 242 } 243 } 244 } 245 } 246 } else { 247 for (IBase nextElement : values) { 248 BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextElement.getClass()); 249 List<T> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()), theWantedClass); 250 retVal.addAll(foundValues); 251 } 252 } 253 return retVal; 254 } 255 256 public List<Object> getValues(IBaseResource theResource, String thePath) { 257 Class<Object> wantedClass = Object.class; 258 259 return getValues(theResource, thePath, wantedClass); 260 261 } 262 263 public <T> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass) { 264 RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); 265 266 BaseRuntimeElementCompositeDefinition<?> currentDef = def; 267 Object currentObj = theResource; 268 269 List<String> parts = Arrays.asList(thePath.split("\\.")); 270 List<String> subList = parts.subList(1, parts.size()); 271 if (subList.size() < 1) { 272 throw new ConfigurationException("Invalid path: " + thePath); 273 } 274 return getValues(currentDef, currentObj, subList, theWantedClass); 275 } 276 277 /** 278 * Returns <code>true</code> if <code>theSource</code> is in the compartment named <code>theCompartmentName</code> 279 * belonging to resource <code>theTarget</code> 280 * 281 * @param theCompartmentName The name of the compartment 282 * @param theSource The potential member of the compartment 283 * @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} 284 * @return <code>true</code> if <code>theSource</code> is in the compartment 285 * @throws IllegalArgumentException If theTarget does not contain both a resource type and ID 286 */ 287 public boolean isSourceInCompartmentForTarget(String theCompartmentName, IBaseResource theSource, IIdType theTarget) { 288 Validate.notBlank(theCompartmentName, "theCompartmentName must not be null or blank"); 289 Validate.notNull(theSource, "theSource must not be null"); 290 Validate.notNull(theTarget, "theTarget must not be null"); 291 Validate.notBlank(defaultString(theTarget.getResourceType()), "theTarget must have a populated resource type (theTarget.getResourceType() does not return a value)"); 292 Validate.notBlank(defaultString(theTarget.getIdPart()), "theTarget must have a populated ID (theTarget.getIdPart() does not return a value)"); 293 294 String wantRef = theTarget.toUnqualifiedVersionless().getValue(); 295 296 RuntimeResourceDefinition sourceDef = myContext.getResourceDefinition(theSource); 297 if (theSource.getIdElement().hasIdPart()) { 298 if (wantRef.equals(sourceDef.getName() + '/' + theSource.getIdElement().getIdPart())) { 299 return true; 300 } 301 } 302 303 List<RuntimeSearchParam> params = sourceDef.getSearchParamsForCompartmentName(theCompartmentName); 304 for (RuntimeSearchParam nextParam : params) { 305 for (String nextPath : nextParam.getPathsSplit()) { 306 for (IBaseReference nextValue : getValues(theSource, nextPath, IBaseReference.class)) { 307 String nextRef = nextValue.getReferenceElement().toUnqualifiedVersionless().getValue(); 308 if (wantRef.equals(nextRef)) { 309 return true; 310 } 311 } 312 } 313 } 314 315 return false; 316 } 317 318 private void visit(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor2 theCallback, List<IBase> theContainingElementPath, 319 List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) { 320 if (theChildDefinition != null) { 321 theChildDefinitionPath.add(theChildDefinition); 322 } 323 theContainingElementPath.add(theElement); 324 theElementDefinitionPath.add(theDefinition); 325 326 theCallback.acceptElement(theElement, Collections.unmodifiableList(theContainingElementPath), Collections.unmodifiableList(theChildDefinitionPath), 327 Collections.unmodifiableList(theElementDefinitionPath)); 328 329 /* 330 * Visit undeclared extensions 331 */ 332 if (theElement instanceof ISupportsUndeclaredExtensions) { 333 ISupportsUndeclaredExtensions containingElement = (ISupportsUndeclaredExtensions) theElement; 334 for (ExtensionDt nextExt : containingElement.getUndeclaredExtensions()) { 335 theContainingElementPath.add(nextExt); 336 theCallback.acceptUndeclaredExtension(nextExt, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath); 337 theContainingElementPath.remove(theContainingElementPath.size() - 1); 338 } 339 } 340 341 /* 342 * Now visit the children of the given element 343 */ 344 switch (theDefinition.getChildType()) { 345 case ID_DATATYPE: 346 case PRIMITIVE_XHTML_HL7ORG: 347 case PRIMITIVE_XHTML: 348 case PRIMITIVE_DATATYPE: 349 // These are primitive types, so we don't need to visit their children 350 break; 351 case RESOURCE: 352 case RESOURCE_BLOCK: 353 case COMPOSITE_DATATYPE: { 354 BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) theDefinition; 355 for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) { 356 List<? extends IBase> values = nextChild.getAccessor().getValues(theElement); 357 if (values != null) { 358 for (IBase nextValue : values) { 359 if (nextValue == null) { 360 continue; 361 } 362 if (nextValue.isEmpty()) { 363 continue; 364 } 365 BaseRuntimeElementDefinition<?> childElementDef; 366 childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass()); 367 368 if (childElementDef == null) { 369 StringBuilder b = new StringBuilder(); 370 b.append("Found value of type["); 371 b.append(nextValue.getClass().getSimpleName()); 372 b.append("] which is not valid for field["); 373 b.append(nextChild.getElementName()); 374 b.append("] in "); 375 b.append(childDef.getName()); 376 b.append(" - Valid types: "); 377 for (Iterator<String> iter = new TreeSet<String>(nextChild.getValidChildNames()).iterator(); iter.hasNext();) { 378 BaseRuntimeElementDefinition<?> childByName = nextChild.getChildByName(iter.next()); 379 b.append(childByName.getImplementingClass().getSimpleName()); 380 if (iter.hasNext()) { 381 b.append(", "); 382 } 383 } 384 throw new DataFormatException(b.toString()); 385 } 386 387 if (nextChild instanceof RuntimeChildDirectResource) { 388 // Don't descend into embedded resources 389 theContainingElementPath.add(nextValue); 390 theChildDefinitionPath.add(nextChild); 391 theElementDefinitionPath.add(myContext.getElementDefinition(nextValue.getClass())); 392 theCallback.acceptElement(nextValue, Collections.unmodifiableList(theContainingElementPath), Collections.unmodifiableList(theChildDefinitionPath), 393 Collections.unmodifiableList(theElementDefinitionPath)); 394 theChildDefinitionPath.remove(theChildDefinitionPath.size() - 1); 395 theContainingElementPath.remove(theContainingElementPath.size() - 1); 396 theElementDefinitionPath.remove(theElementDefinitionPath.size() - 1); 397 } else { 398 visit(nextValue, nextChild, childElementDef, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath); 399 } 400 } 401 } 402 } 403 break; 404 } 405 case CONTAINED_RESOURCES: { 406 BaseContainedDt value = (BaseContainedDt) theElement; 407 for (IResource next : value.getContainedResources()) { 408 BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(next); 409 visit(next, null, def, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath); 410 } 411 break; 412 } 413 case EXTENSION_DECLARED: 414 case UNDECL_EXT: { 415 throw new IllegalStateException("state should not happen: " + theDefinition.getChildType()); 416 } 417 case CONTAINED_RESOURCE_LIST: { 418 if (theElement != null) { 419 BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theElement.getClass()); 420 visit(theElement, null, def, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath); 421 } 422 break; 423 } 424 } 425 426 if (theChildDefinition != null) { 427 theChildDefinitionPath.remove(theChildDefinitionPath.size() - 1); 428 } 429 theContainingElementPath.remove(theContainingElementPath.size() - 1); 430 theElementDefinitionPath.remove(theElementDefinitionPath.size() - 1); 431 } 432 433 /** 434 * Visit all elements in a given resource 435 * 436 * <p> 437 * 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. 438 * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource) 439 * </p> 440 * 441 * @param theResource 442 * The resource to visit 443 * @param theVisitor 444 * The visitor 445 */ 446 public void visit(IBaseResource theResource, IModelVisitor theVisitor) { 447 BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource); 448 visit(new IdentityHashMap<Object, Object>(), theResource, theResource, null, null, def, theVisitor); 449 } 450 451 /** 452 * Visit all elements in a given resource 453 * 454 * THIS ALTERNATE METHOD IS STILL EXPERIMENTAL 455 * 456 * <p> 457 * 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. 458 * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource) 459 * </p> 460 * 461 * @param theResource 462 * The resource to visit 463 * @param theVisitor 464 * The visitor 465 */ 466 void visit(IBaseResource theResource, IModelVisitor2 theVisitor) { 467 BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource); 468 visit(theResource, null, def, theVisitor, new ArrayList<IBase>(), new ArrayList<BaseRuntimeChildDefinition>(), new ArrayList<BaseRuntimeElementDefinition<?>>()); 469 } 470 471 private void visit(IdentityHashMap<Object, Object> theStack, IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, 472 BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor theCallback) { 473 List<String> pathToElement = addNameToList(thePathToElement, theChildDefinition); 474 475 if (theStack.put(theElement, theElement) != null) { 476 return; 477 } 478 479 theCallback.acceptElement(theResource, theElement, pathToElement, theChildDefinition, theDefinition); 480 481 BaseRuntimeElementDefinition<?> def = theDefinition; 482 if (def.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) { 483 def = myContext.getElementDefinition(theElement.getClass()); 484 } 485 486 if (theElement instanceof IBaseReference) { 487 IBaseResource target = ((IBaseReference)theElement).getResource(); 488 if (target != null) { 489 if (target.getIdElement().hasIdPart() == false || target.getIdElement().isLocal()) { 490 RuntimeResourceDefinition targetDef = myContext.getResourceDefinition(target); 491 visit(theStack, target, target, pathToElement, null, targetDef, theCallback); 492 } 493 } 494 } 495 496 switch (def.getChildType()) { 497 case ID_DATATYPE: 498 case PRIMITIVE_XHTML_HL7ORG: 499 case PRIMITIVE_XHTML: 500 case PRIMITIVE_DATATYPE: 501 // These are primitive types 502 break; 503 case RESOURCE: 504 case RESOURCE_BLOCK: 505 case COMPOSITE_DATATYPE: { 506 BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def; 507 for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) { 508 509 List<?> values = nextChild.getAccessor().getValues(theElement); 510 if (values != null) { 511 for (Object nextValueObject : values) { 512 IBase nextValue; 513 try { 514 nextValue = (IBase) nextValueObject; 515 } catch (ClassCastException e) { 516 String s = "Found instance of " + nextValueObject.getClass() + " - Did you set a field value to the incorrect type? Expected " + IBase.class.getName(); 517 throw new ClassCastException(s); 518 } 519 if (nextValue == null) { 520 continue; 521 } 522 if (nextValue.isEmpty()) { 523 continue; 524 } 525 BaseRuntimeElementDefinition<?> childElementDef; 526 childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass()); 527 528 if (childElementDef == null) { 529 childElementDef = myContext.getElementDefinition(nextValue.getClass()); 530 } 531 532 if (nextChild instanceof RuntimeChildDirectResource) { 533 // Don't descend into embedded resources 534 theCallback.acceptElement(theResource, nextValue, null, nextChild, childElementDef); 535 } else { 536 visit(theStack, theResource, nextValue, pathToElement, nextChild, childElementDef, theCallback); 537 } 538 } 539 } 540 } 541 break; 542 } 543 case CONTAINED_RESOURCES: { 544 BaseContainedDt value = (BaseContainedDt) theElement; 545 for (IResource next : value.getContainedResources()) { 546 def = myContext.getResourceDefinition(next); 547 visit(theStack, next, next, pathToElement, null, def, theCallback); 548 } 549 break; 550 } 551 case CONTAINED_RESOURCE_LIST: 552 case EXTENSION_DECLARED: 553 case UNDECL_EXT: { 554 throw new IllegalStateException("state should not happen: " + def.getChildType()); 555 } 556 } 557 558 theStack.remove(theElement); 559 560 } 561 562}