001package org.hl7.fhir.r5.profilemodel; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, \ 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this \ 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, \ 013 this list of conditions and the following disclaimer in the documentation \ 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND \ 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \ 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. \ 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, \ 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \ 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR \ 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \ 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \ 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \ 028 POSSIBILITY OF SUCH DAMAGE. 029 */ 030 031import java.util.ArrayList; 032import java.util.List; 033 034import org.apache.commons.lang3.NotImplementedException; 035import org.hl7.fhir.exceptions.DefinitionException; 036import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 037import org.hl7.fhir.r5.context.ContextUtilities; 038import org.hl7.fhir.r5.context.IWorkerContext; 039import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; 040import org.hl7.fhir.r5.model.Base; 041import org.hl7.fhir.r5.model.CanonicalType; 042import org.hl7.fhir.r5.model.ElementDefinition; 043import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; 044import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent; 045import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; 046import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; 047import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 048import org.hl7.fhir.r5.model.Resource; 049import org.hl7.fhir.r5.model.ResourceFactory; 050import org.hl7.fhir.r5.model.StructureDefinition; 051import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; 052import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 053import org.hl7.fhir.utilities.Utilities; 054 055/** 056 * Factory class for the ProfiledElement sub-system 057 * 058 * *** NOTE: This sub-system is still under development *** 059 * 060 * This subsystem takes a profile and creates a view of the profile that stitches 061 * all the parts together, and presents it as a seamless tree. There's two views: 062 * 063 * - definition: A logical view of the contents of the profile 064 * - instance: a logical view of a resource that conforms to the profile 065 * 066 * The tree of elements in the profile model is different to the the base resource: 067 * - some elements are removed (max = 0) 068 * - extensions are turned into named elements 069 * - slices are turned into named elements 070 * - element properties - doco, cardinality, binding etc is updated for what the profile says 071 * 072 * Definition 073 * ---------- 074 * This presents a single view of the contents of a resource as specified by 075 * the profile. It's suitable for use in any kind of tree view. 076 * 077 * Each node has a unique name amongst it's siblings, but this name may not be 078 * the name in the instance, since slicing splits up a single named element into 079 * different definitions. 080 * 081 * Each node has: 082 * - name (unique amongst siblings) 083 * - schema name (the actual name in the instance) 084 * - min cardinality 085 * - max cardinality 086 * - short documentation (for the tree view) 087 * - full documentation (markdown source) 088 * - profile definition - the full definition in the profile 089 * - base definition - the full definition at the resource level 090 * - types() - a list of possible types 091 * - children(type) - a list of child nodes for the provided type 092 * - expansion - if there's a binding, the codes in the expansion based on the binding 093 * 094 * Note that the tree may not have leaves; the trees recurse indefinitely because 095 * extensions have extensions etc. So you can't do a depth-first search of the tree 096 * without some kind of decision to stop at a given point. 097 * 098 * Instance 099 * -------- 100 * 101 * todo 102 * 103 * @author grahamegrieve 104 * 105 */ 106public class PEBuilder { 107 108 public enum PEElementPropertiesPolicy { 109 NONE, EXTENSION, EXTENSION_ID 110 } 111 112 private IWorkerContext context; 113 private ProfileUtilities pu; 114 private ContextUtilities cu; 115 private PEElementPropertiesPolicy elementProps; 116 private boolean fixedPropsDefault; 117 private FHIRPathEngine fpe; 118 119 /** 120 * @param context - must be loaded with R5 definitions 121 * @param elementProps - whether to include Element.id and Element.extension in the tree. Recommended choice: Extension 122 */ 123 public PEBuilder(IWorkerContext context, PEElementPropertiesPolicy elementProps, boolean fixedPropsDefault) { 124 super(); 125 this.context = context; 126 this.elementProps = elementProps; 127 this.fixedPropsDefault = fixedPropsDefault; 128 pu = new ProfileUtilities(context, null, null); 129 cu = new ContextUtilities(context); 130 fpe = new FHIRPathEngine(context, pu); 131 } 132 133 /** 134 * Given a profile, return a tree of the elements defined in the profile model. This builds the profile model 135 * for the provided version of the nominated profile 136 * 137 * The tree of elements in the profile model is different to those defined in the base resource: 138 * - some elements are removed (max = 0) 139 * - extensions are turned into named elements 140 * - slices are turned into named elements 141 * - element properties - doco, cardinality, binding etc is updated for what the profile says 142 * 143 * Warning: profiles and resources are recursive; you can't iterate this tree until it you get 144 * to the leaves because there are nodes that don't terminate (extensions have extensions) 145 * 146 */ 147 public PEDefinition buildPEDefinition(StructureDefinition profile) { 148 if (!profile.hasSnapshot()) { 149 throw new DefinitionException("Profile '"+profile.getVersionedUrl()+"' does not have a snapshot"); 150 } 151 return new PEDefinitionResource(this, profile, profile.getName()); 152 } 153 154 /** 155 * Given a profile, return a tree of the elements defined in the profile model. This builds the profile model 156 * for the latest version of the nominated profile 157 * 158 * The tree of elements in the profile model is different to those defined in the base resource: 159 * - some elements are removed (max = 0) 160 * - extensions are turned into named elements 161 * - slices are turned into named elements 162 * - element properties - doco, cardinality, binding etc is updated for what the profile says 163 * 164 * Warning: profiles and resources are recursive; you can't iterate this tree until it you get 165 * to the leaves because there are nodes that don't terminate (extensions have extensions) 166 * 167 */ 168 public PEDefinition buildPEDefinition(String url) { 169 StructureDefinition profile = getProfile(url); 170 if (profile == null) { 171 throw new DefinitionException("Unable to find profile for URL '"+url+"'"); 172 } 173 if (!profile.hasSnapshot()) { 174 throw new DefinitionException("Profile '"+url+"' does not have a snapshot"); 175 } 176 return new PEDefinitionResource(this, profile, profile.getName()); 177 } 178 179 /** 180 * Given a profile, return a tree of the elements defined in the profile model. This builds the profile model 181 * for the nominated version of the nominated profile 182 * 183 * The tree of elements in the profile model is different to the the base resource: 184 * - some elements are removed (max = 0) 185 * - extensions are turned into named elements 186 * - slices are turned into named elements 187 * - element properties - doco, cardinality, binding etc is updated for what the profile says 188 * 189 * Warning: profiles and resources can be recursive; you can't iterate this tree until it you get 190 * to the leaves because you will never get to a child that doesn't have children 191 * 192 */ 193 public PEDefinition buildPEDefinition(String url, String version) { 194 StructureDefinition profile = getProfile(url, version); 195 if (profile == null) { 196 throw new DefinitionException("Unable to find profile for URL '"+url+"'"); 197 } 198 if (!profile.hasSnapshot()) { 199 throw new DefinitionException("Profile '"+url+"' does not have a snapshot"); 200 } 201 return new PEDefinitionResource(this, profile, profile.getName()); 202 } 203 204 /** 205 * Given a resource and a profile, return a tree of instance data as defined by the profile model 206 * using the latest version of the profile 207 * 208 * The tree is a facade to the underlying resource - all actual data is stored against the resource, 209 * and retrieved on the fly from the resource, so that applications can work at either level, as 210 * convenient. 211 * 212 * Note that there's a risk that deleting something through the resource while holding 213 * a handle to a PEInstance that is a facade on what is deleted leaves an orphan facade 214 * that will continue to function, but is making changes to resource content that is no 215 * longer part of the resource 216 * 217 */ 218 public PEInstance buildPEInstance(String url, Resource resource) { 219 PEDefinition defn = buildPEDefinition(url); 220 return loadInstance(defn, resource); 221 } 222 223 /** 224 * Given a resource and a profile, return a tree of instance data as defined by the profile model 225 * using the provided version of the profile 226 * 227 * The tree is a facade to the underlying resource - all actual data is stored against the resource, 228 * and retrieved on the fly from the resource, so that applications can work at either level, as 229 * convenient. 230 * 231 * Note that there's a risk that deleting something through the resource while holding 232 * a handle to a PEInstance that is a facade on what is deleted leaves an orphan facade 233 * that will continue to function, but is making changes to resource content that is no 234 * longer part of the resource 235 * 236 */ 237 public PEInstance buildPEInstance(StructureDefinition profile, Resource resource) { 238 PEDefinition defn = buildPEDefinition(profile); 239 return loadInstance(defn, resource); 240 } 241 242 /** 243 * Given a resource and a profile, return a tree of instance data as defined by the profile model 244 * using the nominated version of the profile 245 * 246 * The tree is a facade to the underlying resource - all actual data is stored against the resource, 247 * and retrieved on the fly from the resource, so that applications can work at either level, as 248 * convenient. 249 * 250 * Note that there's a risk that deleting something through the resource while holding 251 * a handle to a PEInstance that is a facade on what is deleted leaves an orphan facade 252 * that will continue to function, but is making changes to resource content that is no 253 * longer part of the resource 254 */ 255 public PEInstance buildPEInstance(String url, String version, Resource resource) { 256 PEDefinition defn = buildPEDefinition(url, version); 257 return loadInstance(defn, resource); 258 } 259 260 /** 261 * For the current version of a profile, construct a resource and fill out any fixed or required elements 262 * 263 * Note that fixed values are filled out irrespective of the value of fixedProps when the builder is created 264 * 265 * @param url identifies the profile 266 * @param version identifies the version of the profile 267 * @param meta whether to mark the profile in Resource.meta.profile 268 * @return constructed resource 269 */ 270 public Resource createResource(String url, String version, boolean meta) { 271 PEDefinition definition = buildPEDefinition(url, version); 272 Resource res = ResourceFactory.createResource(definition.types().get(0).getType()); 273 populateByProfile(res, definition); 274 if (meta) { 275 res.getMeta().addProfile(definition.profile.getUrl()); 276 } 277 return res; 278 } 279 280 /** 281 * For the provided version of a profile, construct a resource and fill out any fixed or required elements 282 * 283 * Note that fixed values are filled out irrespective of the value of fixedProps when the builder is created 284 * 285 * @param profile the profile 286 * @param meta whether to mark the profile in Resource.meta.profile 287 * @return constructed resource 288 */ 289 public Resource createResource(StructureDefinition profile, boolean meta) { 290 PEDefinition definition = buildPEDefinition(profile); 291 Resource res = ResourceFactory.createResource(definition.types().get(0).getType()); 292 populateByProfile(res, definition); 293 if (meta) { 294 res.getMeta().addProfile(definition.profile.getUrl()); 295 } 296 return res; 297 } 298 299 /** 300 * For the current version of a profile, construct a resource and fill out any fixed or required elements 301 * 302 * Note that fixed values are filled out irrespective of the value of fixedProps when the builder is created 303 * 304 * @param url identifies the profile 305 * @param meta whether to mark the profile in Resource.meta.profile 306 * @return constructed resource 307 */ 308 public Resource createResource(String url, boolean meta) { 309 PEDefinition definition = buildPEDefinition(url); 310 Resource res = ResourceFactory.createResource(definition.types().get(0).getType()); 311 populateByProfile(res, definition); 312 if (meta) { 313 res.getMeta().addProfile(definition.profile.getUrl()); 314 } 315 return res; 316 } 317 318 319 320 // -- methods below here are only used internally to the package 321 322 private StructureDefinition getProfile(String url) { 323 return context.fetchResource(StructureDefinition.class, url); 324 } 325 326 327 private StructureDefinition getProfile(String url, String version) { 328 return context.fetchResource(StructureDefinition.class, url, version); 329 } 330// 331// protected List<PEDefinition> listChildren(boolean allFixed, StructureDefinition profileStructure, ElementDefinition definition, TypeRefComponent t, CanonicalType u) { 332// // TODO Auto-generated method stub 333// return null; 334// } 335 336 protected List<PEDefinition> listChildren(boolean allFixed, PEDefinition parent, StructureDefinition profileStructure, ElementDefinition definition, String url, String... omitList) { 337 StructureDefinition profile = profileStructure; 338 List<ElementDefinition> list = pu.getChildList(profile, definition); 339 if (definition.getType().size() == 1 || (!definition.getPath().contains(".")) || list.isEmpty()) { 340 assert url == null || checkType(definition, url); 341 List<PEDefinition> res = new ArrayList<>(); 342 if (list.size() == 0) { 343 profile = context.fetchResource(StructureDefinition.class, url); 344 list = pu.getChildList(profile, profile.getSnapshot().getElementFirstRep()); 345 } 346 if (list.size() > 0) { 347 int i = 0; 348 while (i < list.size()) { 349 ElementDefinition defn = list.get(i); 350 if (!defn.getMax().equals("0") && (allFixed || include(defn))) { 351 if (passElementPropsCheck(defn) && !Utilities.existsInList(defn.getName(), omitList)) { 352 PEDefinitionElement pe = new PEDefinitionElement(this, profile, defn, parent.path()); 353 pe.setRecursing(definition == defn || (profile.getDerivation() == TypeDerivationRule.SPECIALIZATION && profile.getType().equals("Extension"))); 354 if (context.isPrimitiveType(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) { 355 pe.setMustHaveValue(definition.getMustHaveValue()); 356 } 357 pe.setInFixedValue(definition.hasFixed() || definition.hasPattern() || parent.isInFixedValue()); 358 if (defn.hasSlicing()) { 359 if (defn.getSlicing().getRules() != SlicingRules.CLOSED) { 360 res.add(pe); 361 pe.setSlicer(true); 362 } 363 i++; 364 while (i < list.size() && list.get(i).getPath().equals(defn.getPath())) { 365 StructureDefinition ext = getExtensionDefinition(list.get(i)); 366 if (ext != null) { 367 res.add(new PEDefinitionExtension(this, list.get(i).getSliceName(), profile, list.get(i), defn, ext, parent.path())); 368 } else if (isTypeSlicing(defn)) { 369 res.add(new PEDefinitionTypeSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); 370 } else { 371 if (ProfileUtilities.isComplexExtension(profile) && defn.getPath().endsWith(".extension")) { 372 res.add(new PEDefinitionSubExtension(this, profile, list.get(i), parent.path())); 373 } else { 374 res.add(new PEDefinitionSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); 375 } 376 } 377 i++; 378 } 379 } else { 380 res.add(pe); 381 i++; 382 } 383 } else { 384 i++; 385 } 386 } else { 387 i++; 388 } 389 } 390 } 391 return res; 392 } else if (list.isEmpty()) { 393 throw new DefinitionException("not done yet!"); 394 } else { 395 throw new DefinitionException("not done yet"); 396 } 397 } 398 399 protected PEDefinition makeChild(PEDefinition parent, StructureDefinition profileStructure, ElementDefinition definition) { 400 PEDefinitionElement pe = new PEDefinitionElement(this, profileStructure, definition, parent.path()); 401 if (context.isPrimitiveType(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) { 402 pe.setMustHaveValue(definition.getMustHaveValue()); 403 } 404 pe.setInFixedValue(definition.hasFixed() || definition.hasPattern() || parent.isInFixedValue()); 405 return pe; 406 } 407 408 private boolean passElementPropsCheck(ElementDefinition bdefn) { 409 switch (elementProps) { 410 case EXTENSION: 411 return !Utilities.existsInList(bdefn.getBase().getPath(), "Element.id"); 412 case NONE: 413 return !Utilities.existsInList(bdefn.getBase().getPath(), "Element.id", "Element.extension"); 414 case EXTENSION_ID: 415 default: 416 return true; 417 } 418 } 419 420 private boolean isTypeSlicing(ElementDefinition defn) { 421 ElementDefinitionSlicingComponent sl = defn.getSlicing(); 422 return sl.getRules() == SlicingRules.CLOSED && sl.getDiscriminator().size() == 1 && 423 sl.getDiscriminatorFirstRep().getType() == DiscriminatorType.TYPE && "$this".equals(sl.getDiscriminatorFirstRep().getPath()); 424 } 425 426 private boolean include(ElementDefinition defn) { 427 if (fixedPropsDefault) { 428 return true; 429 } else { 430 return !(defn.hasFixed() || defn.hasPattern()); 431 } 432 } 433 434 protected List<PEDefinition> listSlices(StructureDefinition profileStructure, ElementDefinition definition, PEDefinition parent) { 435 List<ElementDefinition> list = pu.getSliceList(profileStructure, definition); 436 List<PEDefinition> res = new ArrayList<>(); 437 for (ElementDefinition ed : list) { 438 if (profileStructure.getDerivation() == TypeDerivationRule.CONSTRAINT && profileStructure.getType().equals("Extension")) { 439 res.add(new PEDefinitionSubExtension(this, profileStructure, ed, parent.path())); 440 } else { 441 PEDefinitionElement pe = new PEDefinitionElement(this, profileStructure, ed, parent.path()); 442 pe.setRecursing(definition == ed || (profileStructure.getDerivation() == TypeDerivationRule.SPECIALIZATION && profileStructure.getType().equals("Extension"))); 443 res.add(pe); 444 } 445 } 446 return res; 447 } 448 449 450 private boolean checkType(ElementDefinition defn, String url) { 451 for (TypeRefComponent t : defn.getType()) { 452 if (("http://hl7.org/fhir/StructureDefinition/"+t.getWorkingCode()).equals(url)) { 453 return true; 454 } 455 for (CanonicalType u : t.getProfile()) { 456 if (url.equals(u.getValue())) { 457 return true; 458 } 459 } 460 } 461 return !defn.getPath().contains("."); 462 } 463 464 465 private StructureDefinition getExtensionDefinition(ElementDefinition ed) { 466 if ("Extension".equals(ed.getTypeFirstRep().getWorkingCode()) && ed.getTypeFirstRep().getProfile().size() == 1) { 467 return context.fetchResource(StructureDefinition.class, ed.getTypeFirstRep().getProfile().get(0).asStringValue()); 468 } else { 469 return null; 470 } 471 } 472 473 474 private ElementDefinition getByName(List<ElementDefinition> blist, String name) { 475 for (ElementDefinition ed : blist) { 476 if (name.equals(ed.getName())) { 477 return ed; 478 } 479 } 480 return null; 481 } 482 483 484 protected PEType makeType(TypeRefComponent t) { 485 if (t.hasProfile()) { 486 StructureDefinition sd = context.fetchResource(StructureDefinition.class, t.getProfile().get(0).getValue()); 487 if (sd == null) { 488 return new PEType(tail(t.getProfile().get(0).getValue()), t.getWorkingCode(), t.getProfile().get(0).getValue()); 489 } else { 490 return new PEType(sd.getName(), t.getWorkingCode(), t.getProfile().get(0).getValue()); 491 } 492 } else { 493 return makeType(t.getWorkingCode()); 494 } 495 } 496 497 protected PEType makeType(TypeRefComponent t, CanonicalType u) { 498 StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue()); 499 if (sd == null) { 500 return new PEType(tail(u.getValue()), t.getWorkingCode(), u.getValue()); 501 } else { 502 return new PEType(sd.getName(), t.getWorkingCode(), u.getValue()); 503 } 504 } 505 506 507 protected PEType makeType(String tn, String url) { 508 return new PEType(tn, tn, url); 509 } 510 511 protected PEType makeType(String tn) { 512 return new PEType(tn, tn, "http://hl7.org/fhir/StructureDefinition/"+ tn); 513 } 514 515 private String tail(String value) { 516 return value.contains("/") ? value.substring(value.lastIndexOf("/")+1) : value; 517 } 518 519 protected List<ElementDefinition> getChildren(StructureDefinition profileStructure, ElementDefinition definition) { 520 return pu.getChildList(profileStructure, definition); 521 } 522 523 private PEInstance loadInstance(PEDefinition defn, Resource resource) { 524 return new PEInstance(this, defn, resource, resource, defn.name()); 525 } 526 527 public IWorkerContext getContext() { 528 return context; 529 } 530 531 protected void populateByProfile(Base base, PEDefinition definition) { 532 if (definition.types().size() == 1) { 533 for (PEDefinition pe : definition.directChildren(true)) { 534 if (pe.hasFixedValue()) { 535 if (pe.definition().hasPattern()) { 536 base.setProperty(pe.schemaName(), pe.definition().getPattern()); 537 } else { 538 base.setProperty(pe.schemaName(), pe.definition().getFixed()); 539 } 540 } else if (!pe.isSlicer() && pe.max() == 1) { 541 for (int i = 0; i < pe.min(); i++) { 542 Base b = null; 543 if (pe.schemaName().endsWith("[x]")) { 544 if (pe.types().size() == 1) { 545 b = base.addChild(pe.schemaName().replace("[x]", Utilities.capitalize(pe.types().get(0).getType()))); 546 } 547 } else if (!pe.isBaseList()) { 548 b = base.makeProperty(pe.schemaName().hashCode(), pe.schemaName()); 549 } else { 550 b = base.addChild(pe.schemaName()); 551 } 552 if (b != null) { 553 populateByProfile(b, pe); 554 } 555 } 556 } 557 } 558 } 559 } 560 561 public String makeSliceExpression(StructureDefinition profile, ElementDefinitionSlicingComponent slicing, ElementDefinition definition) { 562 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" and "); 563 for (ElementDefinitionSlicingDiscriminatorComponent d : slicing.getDiscriminator()) { 564 switch (d.getType()) { 565 case EXISTS: 566 throw new DefinitionException("The discriminator type 'exists' is not supported by the PEBuilder"); 567 case PATTERN: 568 throw new DefinitionException("The discriminator type 'pattern' is not supported by the PEBuilder"); 569 case POSITION: 570 throw new DefinitionException("The discriminator type 'position' is not supported by the PEBuilder"); 571 case PROFILE: 572 throw new DefinitionException("The discriminator type 'profile' is not supported by the PEBuilder"); 573 case TYPE: 574 throw new DefinitionException("The discriminator type 'type' is not supported by the PEBuilder"); 575 case VALUE: 576 String path = d.getPath(); 577 if (path.contains(".")) { 578 throw new DefinitionException("The discriminator path '"+path+"' is not supported by the PEBuilder"); 579 } 580 ElementDefinition ed = getChildElement(profile, definition, path); 581 if (ed == null) { 582 throw new DefinitionException("The discriminator path '"+path+"' could not be resolved by the PEBuilder"); 583 } 584 if (!ed.hasFixed()) { 585 throw new DefinitionException("The discriminator path '"+path+"' has no fixed value - this is not supported by the PEBuilder"); 586 } 587 if (!ed.getFixed().isPrimitive()) { 588 throw new DefinitionException("The discriminator path '"+path+"' has a fixed value that is not a primitive ("+ed.getFixed().fhirType()+") - this is not supported by the PEBuilder"); 589 } 590 b.append(path+" = '"+ed.getFixed().primitiveValue()+"'"); 591 break; 592 case NULL: 593 throw new DefinitionException("The discriminator type 'null' is not supported by the PEBuilder"); 594 default: 595 throw new DefinitionException("The discriminator type '??' is not supported by the PEBuilder"); 596 } 597 } 598 return b.toString(); 599 } 600 601 private ElementDefinition getChildElement(StructureDefinition profile, ElementDefinition definition, String path) { 602 List<ElementDefinition> elements = pu.getChildList(profile, definition); 603 if (elements.size() == 0) { 604 profile = definition.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, definition.getTypeFirstRep().getProfile().get(0).asStringValue()) : 605 context.fetchTypeDefinition(definition.getTypeFirstRep().getWorkingCode()); 606 elements = pu.getChildList(profile, profile.getSnapshot().getElementFirstRep()); 607 } 608 return getByName(elements, path); 609 } 610 611 public List<Base> exec(Resource resource, Base data, String fhirpath) { 612 return fpe.evaluate(this, resource, resource, data, fhirpath); 613 } 614 615 public boolean isResource(String name) { 616 return cu.isResource(name); 617 } 618}