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