
001package org.hl7.fhir.convertors.misc; 002 003import java.io.FileNotFoundException; 004import java.io.IOException; 005import java.util.*; 006 007import org.hl7.fhir.exceptions.DefinitionException; 008import org.hl7.fhir.exceptions.FHIRException; 009import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 010import org.hl7.fhir.r5.context.ContextUtilities; 011import org.hl7.fhir.r5.context.SimpleWorkerContext; 012import org.hl7.fhir.r5.extensions.ExtensionDefinitions; 013import org.hl7.fhir.r5.extensions.ExtensionUtilities; 014import org.hl7.fhir.r5.model.*; 015import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; 016import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; 017import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 018import org.hl7.fhir.r5.model.Enumerations.FHIRVersion; 019import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionContextComponent; 020import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 021import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; 022import org.hl7.fhir.utilities.StandardsStatus; 023import org.hl7.fhir.utilities.Utilities; 024import org.hl7.fhir.utilities.VersionUtilities; 025 026public class ProfileVersionAdaptor { 027 public enum ConversionMessageStatus { 028 ERROR, WARNING, NOTE 029 } 030 031 public static class ConversionMessage { 032 private String message; 033 private ConversionMessageStatus status; 034 public ConversionMessage(String message, ConversionMessageStatus status) { 035 super(); 036 this.message = message; 037 this.status = status; 038 } 039 public String getMessage() { 040 return message; 041 } 042 public ConversionMessageStatus getStatus() { 043 return status; 044 } 045 } 046 047 private SimpleWorkerContext sCtxt; 048 private SimpleWorkerContext tCtxt; 049 private ProfileUtilities tpu; 050 private ContextUtilities tcu; 051 private List<StructureDefinition> snapshotQueue = new ArrayList<>(); 052 053 public ProfileVersionAdaptor(SimpleWorkerContext sourceContext, SimpleWorkerContext targetContext) { 054 super(); 055 this.sCtxt = sourceContext; 056 this.tCtxt = targetContext; 057 if (VersionUtilities.versionMatches(sourceContext.getVersion(), targetContext.getVersion())) { 058 throw new DefinitionException("Cannot convert profile from "+sourceContext.getVersion()+" to "+targetContext.getVersion()); 059 } else if (VersionUtilities.compareVersions(sourceContext.getVersion(), targetContext.getVersion()) < 1) { 060 throw new DefinitionException("Only converts backwards - cannot do "+sourceContext.getVersion()+" to "+targetContext.getVersion()); 061 } 062 tcu = new ContextUtilities(tCtxt); 063 tpu = new ProfileUtilities(tCtxt, null, tcu); 064 065 } 066 067 public StructureDefinition convert(StructureDefinition sd, List<ConversionMessage> log) throws FileNotFoundException, IOException { 068 if (sd.getKind() == StructureDefinitionKind.LOGICAL) { 069 return convertLogical(sd, log); 070 } 071 if (sd.getDerivation() != TypeDerivationRule.CONSTRAINT || !"Extension".equals(sd.getType())) { 072 return null; // nothing to say right now 073 } 074 sd = sd.copy(); 075 convertContext(sd, log); 076 if (sd.getContext().isEmpty()) { 077 log.clear(); 078 log.add(new ConversionMessage("There are no valid contexts for this extension", ConversionMessageStatus.WARNING)); 079 return null; // didn't convert successfully 080 } 081 sd.setFhirVersion(FHIRVersion.fromCode(tCtxt.getVersion())); 082 sd.setSnapshot(null); 083 084 // first pass, targetProfiles 085 for (ElementDefinition ed : sd.getDifferential().getElement()) { 086 for (TypeRefComponent td : ed.getType()) { 087 List<CanonicalType> toRemove = new ArrayList<CanonicalType>(); 088 for (CanonicalType c : td.getTargetProfile()) { 089 String tp = getCorrectedProfile(c); 090 if (tp == null) { 091 log.add(new ConversionMessage("Remove the target profile " + c.getValue() + " from the element " + ed.getIdOrPath(), ConversionMessageStatus.WARNING)); 092 toRemove.add(c); 093 } else if (!tp.equals(c.getValue())) { 094 log.add(new ConversionMessage("Change the target profile " + c.getValue() + " to " + tp + " on the element " + ed.getIdOrPath(), ConversionMessageStatus.WARNING)); 095 c.setValue(tp); 096 } 097 } 098 td.getTargetProfile().removeAll(toRemove); 099 } 100 } 101 // second pass, unsupported primitive data types 102 for (ElementDefinition ed : sd.getDifferential().getElement()) { 103 for (TypeRefComponent tr : ed.getType()) { 104 String mappedDT = getMappedDT(tr.getCode()); 105 if (mappedDT != null) { 106 log.add(new ConversionMessage("Map the type " + tr.getCode() + " to " + mappedDT + " on the element " + ed.getIdOrPath(), ConversionMessageStatus.WARNING)); 107 tr.setCode(mappedDT); 108 } 109 } 110 } 111 112 // third pass, unsupported complex data types 113 ElementDefinition lastExt = null; 114 ElementDefinition group = null; 115 116 int i = 0; 117 while (i < sd.getDifferential().getElement().size()) { 118 ElementDefinition ed = sd.getDifferential().getElement().get(i); 119 if (ed.getPath().contains(".value")) { 120 Map<String, ElementDefinition> children = loadValueChildren(sd.getDifferential(), ed, i); 121 if (ed.getType().size() > 1) { 122 if (ed.getType().removeIf(tr -> !tcu.isDatatype(tr.getWorkingCode()))) { 123 log.add(new ConversionMessage("Remove types from the element " + ed.getIdOrPath(), ConversionMessageStatus.WARNING)); 124 } 125 } else if (ed.getType().size() == 1) { 126 TypeRefComponent tr = ed.getTypeFirstRep(); 127 if (!tcu.isDatatype(tr.getWorkingCode()) || !isValidExtensionType(tr.getWorkingCode())) { 128 if (ed.hasBinding()) { 129 if (!"CodeableReference".equals(tr.getWorkingCode())) { 130 throw new DefinitionException("not handled: Unknown type " + tr.getWorkingCode() + " has a binding"); 131 } 132 } 133 ed.getType().clear(); 134 ed.setMin(0); 135 ed.setMax("0"); 136 lastExt.setDefinition(ed.getDefinition()); 137 lastExt.setShort(ed.getShort()); 138 lastExt.setMax("*"); 139 lastExt.getSlicing().setRules(SlicingRules.OPEN).setOrdered(false).addDiscriminator().setType(DiscriminatorType.VALUE).setPath("url"); 140 StructureDefinition type = sCtxt.fetchTypeDefinition(tr.getCode()); 141 if (type == null) { 142 throw new DefinitionException("unable to find definition for " + tr.getCode()); 143 } 144 log.add(new ConversionMessage("Replace the type " + tr.getCode() + " with a set of extensions for the content of the type along with the _datatype extension", ConversionMessageStatus.WARNING)); 145 int insPoint = sd.getDifferential().getElement().indexOf(lastExt); 146 int offset = 1; 147 148 // a slice extension for _datatype 149 offset = addDatatypeSlice(sd, offset, insPoint, lastExt, tr.getCode()); 150 151 // now, a slice extension for each thing in the data type differential 152 for (ElementDefinition elementFromType : type.getDifferential().getElement()) { 153 if (elementFromType.getPath().contains(".")) { // skip the root 154 ElementDefinition constraintFromExtension = children.get(elementFromType.getPath().substring(elementFromType.getPath().indexOf(".") + 1)); 155 ElementDefinition base = lastExt; 156 int bo = 0; 157 int cc = Utilities.charCount(elementFromType.getPath(), '.'); 158 if (cc > 2) { 159 throw new DefinitionException("type is deeper than 2?"); 160 } else if (cc == 2) { 161 base = group; 162 bo = 2; 163 } else { 164 // nothing 165 } 166 ElementDefinition newBaseElementDefinition = new ElementDefinition(base.getPath()); 167 newBaseElementDefinition.setSliceName(elementFromType.getName()); 168 if (constraintFromExtension != null) { 169 newBaseElementDefinition.setShortElement(constraintFromExtension.hasShort() ? constraintFromExtension.getShortElement() : elementFromType.getShortElement()); 170 newBaseElementDefinition.setDefinitionElement(constraintFromExtension.hasDefinition() ? constraintFromExtension.getDefinitionElement() : elementFromType.getDefinitionElement()); 171 newBaseElementDefinition.setCommentElement(constraintFromExtension.hasComment() ? constraintFromExtension.getCommentElement() : elementFromType.getCommentElement()); 172 newBaseElementDefinition.setMinElement(constraintFromExtension.hasMin() ? constraintFromExtension.getMinElement() : elementFromType.getMinElement()); 173 newBaseElementDefinition.setMaxElement(constraintFromExtension.hasMax() ? constraintFromExtension.getMaxElement() : elementFromType.getMaxElement()); 174 } else { 175 newBaseElementDefinition.setShortElement(elementFromType.getShortElement()); 176 newBaseElementDefinition.setDefinitionElement(elementFromType.getDefinitionElement()); 177 newBaseElementDefinition.setCommentElement(elementFromType.getCommentElement()); 178 newBaseElementDefinition.setMinElement(elementFromType.getMinElement()); 179 newBaseElementDefinition.setMaxElement(elementFromType.getMaxElement()); 180 } 181 182 offset = addDiffElement(sd, insPoint - bo, offset, newBaseElementDefinition); 183 // set the extensions to 0 184 ElementDefinition newExtensionElementDefinition = new ElementDefinition(base.getPath() + ".extension"); 185 newExtensionElementDefinition.setMax("0"); 186 offset = addDiffElement(sd, insPoint - bo, offset, newExtensionElementDefinition); 187 // fix the url 188 ElementDefinition newUrlElementDefinition = new ElementDefinition(base.getPath() + ".url"); 189 newUrlElementDefinition.setFixed(new UriType(elementFromType.getName())); 190 offset = addDiffElement(sd, insPoint - bo, offset, newUrlElementDefinition); 191 // set the value 192 ElementDefinition newValueElementDefinition = new ElementDefinition(base.getPath() + ".value[x]"); 193 newValueElementDefinition.setMin(1); 194 offset = addDiffElement(sd, insPoint - bo, offset, newValueElementDefinition); 195 if (elementFromType.getType().size() == 1 && Utilities.existsInList(elementFromType.getTypeFirstRep().getWorkingCode(), "Element", "BackboneElement")) { 196 newExtensionElementDefinition.setMax("*"); 197 newValueElementDefinition.setMin(0); 198 newValueElementDefinition.setMax("0"); 199 newValueElementDefinition.getType().clear(); 200 group = newExtensionElementDefinition; 201 group.getSlicing().setRules(SlicingRules.OPEN).setOrdered(false).addDiscriminator().setType(DiscriminatorType.VALUE).setPath("url"); 202 } else { 203 Set<String> types = new HashSet<>(); 204 for (TypeRefComponent ttr : (constraintFromExtension != null && constraintFromExtension.hasType() ? constraintFromExtension : elementFromType).getType()) { 205 TypeRefComponent ntr = checkTypeReference(ttr, types); 206 if (ntr != null) { 207 types.add(ntr.getWorkingCode()); 208 newValueElementDefinition.addType(ntr); 209 } 210 } 211 if (newValueElementDefinition.getType().isEmpty()) { 212 throw new DefinitionException("No types?"); 213 } 214 if (ed.hasBinding() && "concept".equals(elementFromType.getName())) { // codeablereference, we have to move the binding down one 215 newValueElementDefinition.setBinding(ed.getBinding()); 216 } else { 217 newValueElementDefinition.setBinding(elementFromType.getBinding()); 218 } 219 } 220 } 221 } 222 } 223 } 224 } 225 if (ed.getPath().endsWith(".extension")) { 226 lastExt = ed; 227 } 228 i++; 229 } 230 if (!log.isEmpty()) { 231 if (!sd.hasExtension(ExtensionDefinitions.EXT_FMM_LEVEL) || ExtensionUtilities.readIntegerExtension(sd, ExtensionDefinitions.EXT_FMM_LEVEL, 0) > 2) { 232 ExtensionUtilities.setCodeExtension(sd, ExtensionDefinitions.EXT_FMM_LEVEL, "2"); 233 } 234 StandardsStatus code = ExtensionUtilities.getStandardsStatus(sd); 235 if (code == StandardsStatus.TRIAL_USE) { 236 ExtensionUtilities.setCodeExtension(sd, ExtensionDefinitions.EXT_STANDARDS_STATUS, "draft"); 237 ExtensionUtilities.setCodeExtension(sd, ExtensionDefinitions.EXT_STANDARDS_STATUS_REASON, "Extensions that have been modified for " + VersionUtilities.getNameForVersion(tCtxt.getVersion()) + " are still draft while real-world experience is collected"); 238 log.add(new ConversionMessage("Note: Extensions that have been modified for " + VersionUtilities.getNameForVersion(tCtxt.getVersion()) + " are still draft while real-world experience is collected", ConversionMessageStatus.NOTE)); 239 } 240 } 241 snapshotQueue.add(sd); 242 tCtxt.cacheResource(sd); 243 return sd; 244 } 245 246 private Map<String, ElementDefinition> loadValueChildren(StructureDefinition.StructureDefinitionDifferentialComponent differential, ElementDefinition ved, int i) { 247 Map<String, ElementDefinition> children = new HashMap<>(); 248 String pathPrefix = ved.getPath(); 249 i++; 250 while (i < differential.getElement().size() && differential.getElement().get(i).getPath().startsWith(pathPrefix)) { 251 ElementDefinition ed = differential.getElement().get(i); 252 children.put(ed.getPath().substring(pathPrefix.length()+1), ed); 253 differential.getElement().remove(i); 254 } 255 return children; 256 } 257 258 public void generateSnapshots(List<ConversionMessage> log) { 259 for (StructureDefinition sd : snapshotQueue) { 260 StructureDefinition base = tCtxt.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); 261 if (base == null) { 262 log.add(new ConversionMessage("Unable to create "+VersionUtilities.getNameForVersion(tCtxt.getVersion())+" version of "+sd.getVersionedUrl()+" because cannot find StructureDefinition for "+sd.getBaseDefinition()+" for version "+tCtxt.getVersion(), ConversionMessageStatus.WARNING)); 263 } else { 264 tpu.generateSnapshot(base, sd, sd.getUrl(), "http://hl7.org/" + VersionUtilities.getNameForVersion(tCtxt.getVersion()) + "/", sd.getName()); 265 } 266 } 267 } 268 269 private StructureDefinition convertLogical(StructureDefinition sdSrc, List<ConversionMessage> log) { 270 StructureDefinition sd = sdSrc.copy(); 271 sd.setFhirVersion(FHIRVersion.fromCode(tCtxt.getVersion())); 272 sd.setSnapshot(null); 273 274 // first pass, targetProfiles 275 for (ElementDefinition ed : sd.getDifferential().getElement()) { 276 for (TypeRefComponent td : ed.getType()) { 277 List<CanonicalType> toRemove = new ArrayList<CanonicalType>(); 278 for (CanonicalType c : td.getTargetProfile()) { 279 String tp = getCorrectedProfile(c); 280 if (tp == null) { 281 log.add(new ConversionMessage("Remove the target profile "+c.getValue()+" from the element "+ed.getIdOrPath(), ConversionMessageStatus.WARNING)); 282 toRemove.add(c); 283 } else if (!tp.equals(c.getValue())) { 284 log.add(new ConversionMessage("Change the target profile "+c.getValue()+" to "+tp+" on the element "+ed.getIdOrPath(), ConversionMessageStatus.WARNING)); 285 c.setValue(tp); 286 } 287 } 288 td.getTargetProfile().removeAll(toRemove); 289 } 290 } 291 // second pass, unsupported primitive data types 292 for (ElementDefinition ed : sd.getDifferential().getElement()) { 293 for (TypeRefComponent tr : ed.getType()) { 294 String mappedDT = getMappedDT(tr.getCode()); 295 if (mappedDT != null) { 296 log.add(new ConversionMessage("Map the type "+tr.getCode()+" to "+mappedDT+" on the element "+ed.getIdOrPath(), ConversionMessageStatus.WARNING)); 297 tr.setCode(mappedDT); 298 } 299 } 300 } 301 302 // third pass, unsupported complex data types 303 for (int i = 0; i < sd.getDifferential().getElement().size(); i++) { 304 ElementDefinition ed = sd.getDifferential().getElement().get(i); 305 if (ed.getType().size() > 1) { 306 if (ed.getType().removeIf(tr -> !tcu.isDatatype(tr.getWorkingCode()))) { 307 log.add(new ConversionMessage("Remove types from the element " + ed.getIdOrPath(), ConversionMessageStatus.WARNING)); 308 } 309 } else if (ed.getType().size() == 1) { 310 TypeRefComponent tr = ed.getTypeFirstRep(); 311 if (!tcu.isDatatype(tr.getWorkingCode()) && !isValidLogicalType(tr.getWorkingCode())) { 312 log.add(new ConversionMessage("Illegal type "+tr.getWorkingCode(), ConversionMessageStatus.ERROR)); 313 return null; 314 } 315 } 316 } 317 318 if (!log.isEmpty()) { 319 if (!sd.hasExtension(ExtensionDefinitions.EXT_FMM_LEVEL) || ExtensionUtilities.readIntegerExtension(sd, ExtensionDefinitions.EXT_FMM_LEVEL, 0) > 2) { 320 ExtensionUtilities.setCodeExtension(sd, ExtensionDefinitions.EXT_FMM_LEVEL, "2"); 321 } 322 ExtensionUtilities.setCodeExtension(sd, ExtensionDefinitions.EXT_STANDARDS_STATUS, "draft"); 323 ExtensionUtilities.setCodeExtension(sd, ExtensionDefinitions.EXT_STANDARDS_STATUS_REASON, "Logical Models that have been modified for "+VersionUtilities.getNameForVersion(tCtxt.getVersion())+" are still draft while real-world experience is collected"); 324 log.add(new ConversionMessage("Note: Logical Models that have been modified for "+VersionUtilities.getNameForVersion(tCtxt.getVersion())+" are still draft while real-world experience is collected", ConversionMessageStatus.NOTE)); 325 } 326 327 StructureDefinition base = tCtxt.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); 328 if (base == null) { 329 base = sCtxt.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); 330 } 331 if (base == null) { 332 throw new FHIRException("Unable to find base for Logical Model from "+sd.getBaseDefinition()); 333 } 334 snapshotQueue.add(sd); 335 tCtxt.cacheResource(sd); 336 return sd; 337 } 338 339 private boolean isValidLogicalType(String code) { 340 StructureDefinition sd = tCtxt.fetchTypeDefinition(code); 341 if (sd != null) { 342 return true; 343 } 344 sd = sCtxt.fetchTypeDefinition(code); 345 if (sd != null && !sd.getSourcePackage().isCore()) { 346 return true; 347 } 348 return false; 349 } 350 351 private int addDatatypeSlice(StructureDefinition sd, int offset, int insPoint, ElementDefinition base, String type) { 352 ElementDefinition ned = new ElementDefinition(base.getPath()); 353 ned.setSliceName("_datatype"); 354 ned.setShort("DataType name '"+type+"' from "+VersionUtilities.getNameForVersion(sCtxt.getVersion())); 355 ned.setDefinition(ned.getShort()); 356 ned.setMin(1); 357 ned.setMax("1"); 358 ned.addType().setCode("Extension").addProfile("http://hl7.org/fhir/StructureDefinition/_datatype"); 359 offset = addDiffElement(sd, insPoint, offset, ned); 360 // // set the extensions to 0 361 // ElementDefinition need = new ElementDefinition(base.getPath()+".extension"); 362 // need.setMax("0"); 363 // offset = addDiffElement(sd, insPoint, offset, need); 364 // // fix the url 365 // ned = new ElementDefinition(base.getPath()+".url"); 366 // ned.setFixed(new UriType("http://hl7.org/fhir/StructureDefinition/_datatype")); 367 // offset = addDiffElement(sd, insPoint, offset, ned); 368 // set the value 369 ned = new ElementDefinition(base.getPath()+".value[x]"); 370 ned.setMin(1); 371 offset = addDiffElement(sd, insPoint, offset, ned); 372 ned.addType().setCode("string"); 373 ned.setFixed(new StringType(type)); 374 return offset; 375 } 376 377 private int addDiffElement(StructureDefinition sd, int insPoint, int offset, ElementDefinition ned) { 378 sd.getDifferential().getElement().add(insPoint+offset, ned); 379 offset++; 380 return offset; 381 } 382 383 private boolean isValidExtensionType(String type) { 384 StructureDefinition extDef = tCtxt.fetchTypeDefinition("Extension"); 385 ElementDefinition ed = extDef.getSnapshot().getElementByPath("Extension.value"); 386 for (TypeRefComponent tr : ed.getType()) { 387 if (type.equals(tr.getCode())) { 388 return true; 389 } 390 } 391 return false; 392 } 393 394 private TypeRefComponent checkTypeReference(TypeRefComponent tr, Set<String> types) { 395 String dt = getMappedDT(tr.getCode()); 396 if (dt != null) { 397 if (types.contains(dt)) { 398 return null; 399 } else { 400 return tr.copy().setCode(dt); 401 } 402 } else if (tcu.isDatatype(tr.getWorkingCode())) { 403 return tr.copy(); 404 } else { 405 return null; 406 } 407 } 408 409 private String getCorrectedProfile(CanonicalType c) { 410 StructureDefinition sd = tCtxt.fetchResource(StructureDefinition.class, c.getValue()); 411 if (sd != null) { 412 return c.getValue(); 413 } 414 // or it might be something defined in the IG or it's dependencies 415 sd = sCtxt.fetchResource(StructureDefinition.class, c.getValue()); 416 if (sd != null && !sd.getSourcePackage().isCore()) { 417 return c.getValue(); 418 } 419 return null; 420 } 421 422 private String getMappedDT(String code) { 423 if (VersionUtilities.isR5Plus(tCtxt.getVersion())) { 424 return code; 425 } 426 if (VersionUtilities.isR4Plus(tCtxt.getVersion())) { 427 switch (code) { 428 case "integer64" : return "version"; 429 default: 430 return null; 431 } 432 } 433 if (VersionUtilities.isR3Ver(tCtxt.getVersion())) { 434 switch (code) { 435 case "integer64" : return "string"; 436 case "canonical" : return "uri"; 437 case "url" : return "uri"; 438 default: 439 return null; 440 } 441 } 442 return null; 443 } 444 445 public void convertContext(StructureDefinition sd, List<ConversionMessage> log) { 446 List<StructureDefinitionContextComponent> toRemove = new ArrayList<>(); 447 for (StructureDefinitionContextComponent ctxt : sd.getContext()) { 448 if (ctxt.getType() != null) { 449 switch (ctxt.getType()) { 450 case ELEMENT: 451 String newPath = adaptPath(ctxt.getExpression()); 452 if (newPath == null) { 453 log.add(new ConversionMessage("Remove the extension context "+ctxt.getExpression(), ConversionMessageStatus.WARNING)); 454 toRemove.add(ctxt); 455 } else if (!newPath.equals(ctxt.getExpression())) { 456 log.add(new ConversionMessage("Adjust the extension context "+ctxt.getExpression()+" to "+newPath, ConversionMessageStatus.WARNING)); 457 ctxt.setExpression(newPath); 458 } 459 break; 460 case EXTENSION: 461 // nothing. for now 462 break; 463 case FHIRPATH: 464 // nothing. for now ? 465 break; 466 case NULL: 467 break; 468 default: 469 break; 470 } 471 } 472 } 473 sd.getContext().removeAll(toRemove); 474 } 475 476 /** 477 * WIP: change a context for an older version, or delete it (= return null) 478 * 479 * ToDo: Use the Cross-Version infrastructure to make this more intelligent 480 * 481 * @param path 482 * @return 483 */ 484 private String adaptPath(String path) { 485 String base = path.contains(".") ? path.substring(0, path.indexOf(".")) : path; 486 StructureDefinition sd = tCtxt.fetchTypeDefinition(base); 487 if (sd == null) { 488 StructureDefinition ssd = sCtxt.fetchTypeDefinition(base); 489 if (ssd != null && ssd.getKind() == StructureDefinitionKind.RESOURCE) { 490 if ("CanonicalResource".equals(base)) { 491 return "CanonicalResource"; 492 } else { 493 return "Basic"; 494 } 495 } else if (ssd != null && ssd.getKind() == StructureDefinitionKind.COMPLEXTYPE) { 496 return null; 497 } else { 498 return null; 499 } 500 } else { 501 ElementDefinition ed = sd.getSnapshot().getElementByPath(base); 502 if (ed == null) { 503 return null; 504 } else { 505 return path; 506 } 507 } 508 } 509 510 public SearchParameter convert(SearchParameter resource, List<ConversionMessage> log) { 511 SearchParameter res = resource.copy(); 512 // todo: translate resource types 513 res.getBase().removeIf(t -> { 514 String rt = t.asStringValue(); 515 boolean r = !tcu.isResource(rt); 516 if (r) { 517 log.add(new ConversionMessage("Remove search base "+rt, ConversionMessageStatus.WARNING)); 518 } 519 return r; 520 } 521 ); 522 return res; 523 } 524 525}