
001package org.hl7.fhir.r5.utils; 002 003import java.util.HashSet; 004import java.util.List; 005import java.util.Set; 006 007import org.hl7.fhir.r5.context.IWorkerContext; 008import org.hl7.fhir.r5.model.CanonicalResource; 009import org.hl7.fhir.r5.model.CanonicalType; 010import org.hl7.fhir.r5.model.CapabilityStatement; 011import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent; 012import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent; 013import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent; 014import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; 015import org.hl7.fhir.r5.model.CodeSystem; 016import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent; 017import org.hl7.fhir.r5.model.ConceptMap; 018import org.hl7.fhir.r5.model.ConceptMap.AdditionalAttributeComponent; 019import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; 020import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent; 021import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; 022import org.hl7.fhir.r5.model.DomainResource; 023import org.hl7.fhir.r5.model.ElementDefinition; 024import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; 025import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; 026import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 027import org.hl7.fhir.r5.model.NamingSystem; 028import org.hl7.fhir.r5.model.OperationDefinition; 029import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionParameterComponent; 030import org.hl7.fhir.r5.model.Questionnaire; 031import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemAnswerOptionComponent; 032import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent; 033import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemEnableWhenComponent; 034import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemInitialComponent; 035import org.hl7.fhir.r5.model.Reference; 036import org.hl7.fhir.r5.model.RelatedArtifact; 037import org.hl7.fhir.r5.model.Resource; 038import org.hl7.fhir.r5.model.SearchParameter; 039import org.hl7.fhir.r5.model.SearchParameter.SearchParameterComponentComponent; 040import org.hl7.fhir.r5.model.StructureDefinition; 041import org.hl7.fhir.r5.model.UsageContext; 042import org.hl7.fhir.r5.model.ValueSet; 043import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; 044import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; 045import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; 046import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent; 047import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent; 048import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 049import org.hl7.fhir.utilities.Utilities; 050 051@MarkedToMoveToAdjunctPackage 052public class ResourceDependencyWalker { 053 054 public interface IResourceDependencyNotifier { 055 public void seeResource(Resource resource, String summaryId); 056 public void brokenLink(String link); 057 } 058 059 public class NullResourceDependencyNotifier implements IResourceDependencyNotifier { 060 061 @Override 062 public void seeResource(Resource resource, String summaryId) { 063 System.out.println(summaryId); 064 } 065 066 @Override 067 public void brokenLink(String link) { 068 System.err.println("Broken Link: " +link); 069 } 070 } 071 072 private IResourceDependencyNotifier notifier = new NullResourceDependencyNotifier(); 073 private IWorkerContext context; 074 private Set<String> processedLinks = new HashSet<>(); 075 private Set<Resource> processedResources = new HashSet<>(); 076 077 public ResourceDependencyWalker(IWorkerContext context, IResourceDependencyNotifier notifier) { 078 super(); 079 this.notifier = notifier; 080 this.context = context; 081 } 082 083 public ResourceDependencyWalker(IWorkerContext context) { 084 super(); 085 this.context = context; 086 } 087 088 private void notify(Resource resource, String prefix) { 089 String summary = null; 090 if (resource instanceof CanonicalResource) { 091 summary = ((CanonicalResource) resource).getVersionedUrl(); 092 } else { 093 summary = resource.fhirType()+"/"+resource.getIdPart(); 094 } 095 if (resource.getSourcePackage() != null) { 096 summary = summary + " from "+resource.getSourcePackage(); 097 } 098 notifier.seeResource(resource, prefix+summary); 099 } 100 101 public void walk(Resource res) { 102 notify(res, "Find Dependencies for "); 103 processedResources.add(res); 104 doWalk(res); 105 } 106 107 private void walkIntoLink(String value, CanonicalResource source) { 108 if (value != null ) { 109 String key = source.getSourcePackage() == null ? value : value+" from "+source.getSourcePackage().getVID(); 110 if (!processedLinks.contains(key)) { 111 processedLinks.add(key); 112 Resource tgt = context.fetchResource(Resource.class, value, source); 113 if (tgt == null && Utilities.charCount(value, '/') == 1) { 114 tgt = context.fetchResourceById(value.substring(0, value.indexOf('/')), value.substring(value.indexOf('/')+1)); 115 } 116 if (tgt == null) { 117 notifier.brokenLink(key); 118 } else { 119 if (!processedResources.contains(tgt) && !isCore(tgt)) { 120 processedResources.add(tgt); 121 notify(tgt, "Depends On "); 122 doWalk(tgt); 123 } 124 } 125 } 126 } 127 } 128 129 private boolean isCore(Resource tgt) { 130 return tgt.hasSourcePackage() && "hl7.fhir.r5.core".equals(tgt.getSourcePackage().getId()); 131 } 132 133 private void doWalk(Resource res) { 134 if (res instanceof StructureDefinition) { 135 walkSD((StructureDefinition) res); 136 } else if (res instanceof ValueSet) { 137 walkVS((ValueSet) res); 138 } else if (res instanceof CodeSystem) { 139 walkCS((CodeSystem) res); 140 } else if (res instanceof CapabilityStatement) { 141 walkCS((CapabilityStatement) res); 142 } else if (res instanceof ConceptMap) { 143 walkCM((ConceptMap) res); 144 } else if (res instanceof NamingSystem) { 145 walkNS((NamingSystem) res); 146 } else if (res instanceof OperationDefinition) { 147 walkOD((OperationDefinition) res); 148 } else if (res instanceof SearchParameter) { 149 walkSP((SearchParameter) res); 150 } else if (res instanceof Questionnaire) { 151 walkQ((Questionnaire) res); 152 } else { 153 throw new Error("Resource "+res.fhirType()+" not Processed yet"); 154 } 155 } 156 157 158 private void walkSP(SearchParameter sp) { 159 walkCR(sp); 160 walkIntoLink(sp.getDerivedFrom(), sp); 161 for (SearchParameterComponentComponent spc : sp.getComponent()) { 162 walkIntoLink(spc.getDefinition(), sp); 163 } 164 } 165 166 private void walkQ(Questionnaire q) { 167 walkCR(q); 168 walkCT(q.getDerivedFrom(), q); 169 for (QuestionnaireItemComponent item : q.getItem()) { 170 walkQItem(item, q); 171 } 172 } 173 174 private void walkQItem(QuestionnaireItemComponent item, Questionnaire q) { 175 walkIntoLink(item.getDefinition(), q); 176 walkIntoLink(item.getAnswerValueSet(), q); 177 for (QuestionnaireItemEnableWhenComponent ew : item.getEnableWhen()) { 178 if (ew.hasAnswerReference()) { 179 walkIntoLink(ew.getAnswerReference().getReference(), q); 180 } 181 } 182 for (QuestionnaireItemAnswerOptionComponent ao : item.getAnswerOption()) { 183 if (ao.hasValueReference()) { 184 walkIntoLink(ao.getValueReference().getReference(), q); 185 } 186 } 187 for (QuestionnaireItemInitialComponent iv : item.getInitial()) { 188 if (iv.hasValueReference()) { 189 walkIntoLink(iv.getValueReference().getReference(), q); 190 } 191 } 192 walkIntoLink(item.getDefinition(), q); 193 for (QuestionnaireItemComponent child : item.getItem()) { 194 walkQItem(child, q); 195 } 196 } 197 198 private void walkOD(OperationDefinition od) { 199 walkCR(od); 200 walkIntoLink(od.getBase(), od); 201 walkIntoLink(od.getInputProfile(), od); 202 walkIntoLink(od.getOutputProfile(), od); 203 204 for (OperationDefinitionParameterComponent p : od.getParameter()) { 205 walkODP(od, p); 206 } 207 } 208 209 private void walkODP(OperationDefinition od, OperationDefinitionParameterComponent p) { 210 walkCT(p.getTargetProfile(), od); 211 walkIntoLink(p.getBinding().getValueSet(), od); 212 for (OperationDefinitionParameterComponent pp : p.getPart()) { 213 walkODP(od, pp); 214 } 215 } 216 217 private void walkNS(NamingSystem ns) { 218 walkCR(ns); 219 } 220 221 private void walkCM(ConceptMap cm) { 222 walkCR(cm); 223 walkRA(cm.getRelatedArtifact(), cm); 224 225 for (org.hl7.fhir.r5.model.ConceptMap.PropertyComponent prop : cm.getProperty()) { 226 walkIntoLink(prop.getUri(), cm); 227 } 228 for (AdditionalAttributeComponent attr : cm.getAdditionalAttribute()) { 229 walkIntoLink(attr.getUri(), cm); 230 } 231 walkIntoLink(cm.getSourceScope().primitiveValue(), cm); 232 walkIntoLink(cm.getTargetScope().primitiveValue(), cm); 233 234 for (ConceptMapGroupComponent group : cm.getGroup()) { 235 walkIntoLink(group.getSource(), cm); 236 walkIntoLink(group.getTarget(), cm); 237 walkIntoLink(group.getUnmapped().getValueSet(), cm); 238 walkIntoLink(group.getUnmapped().getOtherMap(), cm); 239 for (SourceElementComponent elem : group.getElement()) { 240 walkIntoLink(elem.getValueSet(), cm); 241 for (TargetElementComponent tgt : elem.getTarget()) { 242 walkIntoLink(tgt.getValueSet(), cm); 243 } 244 } 245 } 246 } 247 248 private void walkCS(CapabilityStatement cs) { 249 walkCR(cs); 250 walkCT(cs.getInstantiates(), cs); 251 walkCT(cs.getImports(), cs); 252 walkCT(cs.getImplementationGuide(), cs); 253 254 for (CapabilityStatementRestComponent rest : cs.getRest()) { 255 256 for (CapabilityStatementRestResourceComponent res : rest.getResource()) { 257 walkIntoLink(res.getProfile(), cs); 258 walkCT(res.getSupportedProfile(), cs); 259 for (CapabilityStatementRestResourceSearchParamComponent srch : res.getSearchParam()) { 260 walkIntoLink(srch.getDefinition(), cs); 261 } 262 for (CapabilityStatementRestResourceOperationComponent op : res.getOperation()) { 263 walkIntoLink(op.getDefinition(), cs); 264 } 265 } 266 for (CapabilityStatementRestResourceSearchParamComponent srch : rest.getSearchParam()) { 267 walkIntoLink(srch.getDefinition(), cs); 268 } 269 for (CapabilityStatementRestResourceOperationComponent op : rest.getOperation()) { 270 walkIntoLink(op.getDefinition(), cs); 271 } 272 } 273 } 274 275 private void walkCS(CodeSystem cs) { 276 walkCR(cs); 277 walkRA(cs.getRelatedArtifact(), cs); 278 if (cs.hasValueSet()) { 279 walkIntoLink(cs.getValueSet(), cs); 280 } 281 if (cs.hasSupplements()) { 282 walkIntoLink(cs.getSupplements(), cs); 283 } 284 285 for (PropertyComponent p : cs.getProperty()) { 286 if (p.hasUri()) { 287 walkIntoLink(p.getUri(), cs); 288 } 289 } 290 } 291 292 private void walkVS(ValueSet vs) { 293 walkCR(vs); 294 walkRA(vs.getRelatedArtifact(), vs); 295 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 296 walkVSInc(inc, vs); 297 } 298 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 299 walkVSInc(inc, vs); 300 } 301 if (vs.hasExpansion()) { 302 ValueSetExpansionComponent exp = vs.getExpansion(); 303 for (ValueSetExpansionParameterComponent p : exp.getParameter()) { 304 if (p.hasValueUriType()) { 305 walkIntoLink(p.getValueUriType().primitiveValue(), vs); 306 } 307 } 308 for (ValueSetExpansionPropertyComponent p : exp.getProperty()) { 309 if (p.hasUri()) { 310 walkIntoLink(p.getUri(), vs); 311 } 312 } 313 for (ValueSetExpansionContainsComponent cc : exp.getContains()) { 314 walkCC(cc, vs); 315 } 316 } 317 } 318 319 private void walkCC(ValueSetExpansionContainsComponent cc, ValueSet vs) { 320 walkIntoLink(cc.getSystem(), vs); 321 for (ValueSetExpansionContainsComponent ccc : cc.getContains()) { 322 walkCC(ccc, vs); 323 } 324 } 325 326 private void walkVSInc(ConceptSetComponent inc, ValueSet vs) { 327 walkCT(inc.getValueSet(), vs); 328 walkIntoLink(inc.getSystem(), vs); 329 } 330 331 private void walkCT(List<CanonicalType> list, CanonicalResource source) { 332 for (CanonicalType ct : list) { 333 walkIntoLink(ct.getValue(), source); 334 } 335 } 336 337 private void walkRA(List<RelatedArtifact> list, CanonicalResource source) { 338 for (RelatedArtifact ra : list) { 339 walkRA(ra, source); 340 } 341 } 342 343 private void walkRA(RelatedArtifact ra, CanonicalResource source) { 344 if (ra.hasResource()) { 345 walkIntoLink(ra.getResource(), source); 346 } 347 if (ra.hasResourceReference()) { 348 walkIntoLink(ra.getResourceReference().getReference(), source); 349 } 350 } 351 352 private void walkSD(StructureDefinition sd) { 353 walkCR(sd); 354 walkIntoLink(sd.getBaseDefinition(), sd); 355 for (ElementDefinition ed : sd.getDifferential().getElement()) { 356 walkED(ed, sd); 357 } 358 } 359 360 private void walkED(ElementDefinition ed, StructureDefinition sd) { 361 for (TypeRefComponent type : ed.getType()) { 362 if (Utilities.isAbsoluteUrl(type.getCode())) { 363 walkIntoLink(type.getCode(), sd); 364 } 365 walkCT(type.getProfile(), sd); 366 walkCT(type.getTargetProfile(), sd); 367 } 368 walkCT(ed.getValueAlternatives(), sd); 369 if (ed.hasBinding()) { 370 ElementDefinitionBindingComponent b = ed.getBinding(); 371 if (b.hasValueSet()) { 372 walkIntoLink(b.getValueSet(), sd); 373 } 374 for (ElementDefinitionBindingAdditionalComponent ab : b.getAdditional()) { 375 if (ab.hasValueSet()) { 376 walkIntoLink(ab.getValueSet(), sd); 377 } 378 if (ab.hasUsage()) { 379 walkUsage(ab.getUsage(), sd); 380 } 381 } 382 } 383 } 384 385 private void walkUsage(List<UsageContext> usageList, CanonicalResource source) { 386 for (UsageContext usage : usageList) { 387 walkUsage(usage, source); 388 } 389 } 390 391 private void walkUsage(UsageContext usage, CanonicalResource source) { 392 if (usage.hasValueReference()) { 393 walkReference(usage.getValueReference(), source); 394 } 395 } 396 397 private void walkReference(Reference ref, CanonicalResource source) { 398 if (ref.hasReference()) { 399 walkIntoLink(ref.getReference(), source); 400 } 401 } 402 403 private void walkCR(CanonicalResource cr) { 404 walkDR(cr); 405 walkUsage(cr.getUseContext(), cr); 406 } 407 408 private void walkDR(DomainResource dr) { 409 walkRes(dr); 410 for (Resource res : dr.getContained()) { 411 walk(res); 412 } 413 } 414 415 private void walkRes(Resource res) { 416 // nothing 417 } 418 419 420}