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