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 PEDefinitionElement pe = new PEDefinitionElement(this, profile, defn, parent.path()); 350 pe.setRecursing(definition == defn || (profile.getDerivation() == TypeDerivationRule.SPECIALIZATION && profile.getType().equals("Extension"))); 351 if (context.isPrimitiveType(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) { 352 pe.setMustHaveValue(definition.getMustHaveValue()); 353 } 354 pe.setInFixedValue(definition.hasFixed() || definition.hasPattern() || parent.isInFixedValue()); 355 if (defn.hasSlicing()) { 356 if (defn.getSlicing().getRules() != SlicingRules.CLOSED) { 357 res.add(pe); 358 pe.setSlicer(true); 359 } 360 i++; 361 while (i < list.size() && list.get(i).getPath().equals(defn.getPath())) { 362 StructureDefinition ext = getExtensionDefinition(list.get(i)); 363 if (ext != null) { 364 res.add(new PEDefinitionExtension(this, list.get(i).getSliceName(), profile, list.get(i), defn, ext, parent.path())); 365 } else if (isTypeSlicing(defn)) { 366 res.add(new PEDefinitionTypeSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); 367 } else { 368 if (ProfileUtilities.isComplexExtension(profile) && defn.getPath().endsWith(".extension")) { 369 res.add(new PEDefinitionSubExtension(this, profile, list.get(i), parent.path())); 370 } else { 371 res.add(new PEDefinitionSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); 372 } 373 } 374 i++; 375 } 376 } else { 377 res.add(pe); 378 i++; 379 } 380 } else { 381 i++; 382 } 383 } else { 384 i++; 385 } 386 } 387 } 388 return res; 389 } else if (list.isEmpty()) { 390 throw new DefinitionException("not done yet!"); 391 } else { 392 throw new DefinitionException("not done yet"); 393 } 394 } 395 396 protected PEDefinition makeChild(PEDefinition parent, StructureDefinition profileStructure, ElementDefinition definition) { 397 PEDefinitionElement pe = new PEDefinitionElement(this, profileStructure, definition, parent.path()); 398 if (context.isPrimitiveType(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) { 399 pe.setMustHaveValue(definition.getMustHaveValue()); 400 } 401 pe.setInFixedValue(definition.hasFixed() || definition.hasPattern() || parent.isInFixedValue()); 402 return pe; 403 } 404 405 private boolean passElementPropsCheck(ElementDefinition bdefn) { 406 switch (elementProps) { 407 case EXTENSION: 408 return !Utilities.existsInList(bdefn.getBase().getPath(), "Element.id"); 409 case NONE: 410 return !Utilities.existsInList(bdefn.getBase().getPath(), "Element.id", "Element.extension"); 411 case EXTENSION_ID: 412 default: 413 return true; 414 } 415 } 416 417 private boolean isTypeSlicing(ElementDefinition defn) { 418 ElementDefinitionSlicingComponent sl = defn.getSlicing(); 419 return sl.getRules() == SlicingRules.CLOSED && sl.getDiscriminator().size() == 1 && 420 sl.getDiscriminatorFirstRep().getType() == DiscriminatorType.TYPE && "$this".equals(sl.getDiscriminatorFirstRep().getPath()); 421 } 422 423 private boolean include(ElementDefinition defn) { 424 if (fixedPropsDefault) { 425 return true; 426 } else { 427 return !(defn.hasFixed() || defn.hasPattern()); 428 } 429 } 430 431 protected List<PEDefinition> listSlices(StructureDefinition profileStructure, ElementDefinition definition, PEDefinition parent) { 432 List<ElementDefinition> list = pu.getSliceList(profileStructure, definition); 433 List<PEDefinition> res = new ArrayList<>(); 434 for (ElementDefinition ed : list) { 435 if (profileStructure.getDerivation() == TypeDerivationRule.CONSTRAINT && profileStructure.getType().equals("Extension")) { 436 res.add(new PEDefinitionSubExtension(this, profileStructure, ed, parent.path())); 437 } else { 438 PEDefinitionElement pe = new PEDefinitionElement(this, profileStructure, ed, parent.path()); 439 pe.setRecursing(definition == ed || (profileStructure.getDerivation() == TypeDerivationRule.SPECIALIZATION && profileStructure.getType().equals("Extension"))); 440 res.add(pe); 441 } 442 } 443 return res; 444 } 445 446 447 private boolean checkType(ElementDefinition defn, String url) { 448 for (TypeRefComponent t : defn.getType()) { 449 if (("http://hl7.org/fhir/StructureDefinition/"+t.getWorkingCode()).equals(url)) { 450 return true; 451 } 452 for (CanonicalType u : t.getProfile()) { 453 if (url.equals(u.getValue())) { 454 return true; 455 } 456 } 457 } 458 return !defn.getPath().contains("."); 459 } 460 461 462 private StructureDefinition getExtensionDefinition(ElementDefinition ed) { 463 if ("Extension".equals(ed.getTypeFirstRep().getWorkingCode()) && ed.getTypeFirstRep().getProfile().size() == 1) { 464 return context.fetchResource(StructureDefinition.class, ed.getTypeFirstRep().getProfile().get(0).asStringValue()); 465 } else { 466 return null; 467 } 468 } 469 470 471 private ElementDefinition getByName(List<ElementDefinition> blist, String name) { 472 for (ElementDefinition ed : blist) { 473 if (name.equals(ed.getName())) { 474 return ed; 475 } 476 } 477 return null; 478 } 479 480 481 protected PEType makeType(TypeRefComponent t) { 482 if (t.hasProfile()) { 483 StructureDefinition sd = context.fetchResource(StructureDefinition.class, t.getProfile().get(0).getValue()); 484 if (sd == null) { 485 return new PEType(tail(t.getProfile().get(0).getValue()), t.getWorkingCode(), t.getProfile().get(0).getValue()); 486 } else { 487 return new PEType(sd.getName(), t.getWorkingCode(), t.getProfile().get(0).getValue()); 488 } 489 } else { 490 return makeType(t.getWorkingCode()); 491 } 492 } 493 494 protected PEType makeType(TypeRefComponent t, CanonicalType u) { 495 StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue()); 496 if (sd == null) { 497 return new PEType(tail(u.getValue()), t.getWorkingCode(), u.getValue()); 498 } else { 499 return new PEType(sd.getName(), t.getWorkingCode(), u.getValue()); 500 } 501 } 502 503 504 protected PEType makeType(String tn, String url) { 505 return new PEType(tn, tn, url); 506 } 507 508 protected PEType makeType(String tn) { 509 return new PEType(tn, tn, "http://hl7.org/fhir/StructureDefinition/"+ tn); 510 } 511 512 private String tail(String value) { 513 return value.contains("/") ? value.substring(value.lastIndexOf("/")+1) : value; 514 } 515 516 protected List<ElementDefinition> getChildren(StructureDefinition profileStructure, ElementDefinition definition) { 517 return pu.getChildList(profileStructure, definition); 518 } 519 520 private PEInstance loadInstance(PEDefinition defn, Resource resource) { 521 return new PEInstance(this, defn, resource, resource, defn.name()); 522 } 523 524 public IWorkerContext getContext() { 525 return context; 526 } 527 528 protected void populateByProfile(Base base, PEDefinition definition) { 529 if (definition.types().size() == 1) { 530 for (PEDefinition pe : definition.directChildren(true)) { 531 if (pe.fixedValue()) { 532 if (pe.definition().hasPattern()) { 533 base.setProperty(pe.schemaName(), pe.definition().getPattern()); 534 } else { 535 base.setProperty(pe.schemaName(), pe.definition().getFixed()); 536 } 537 } else if (!pe.isSlicer() && pe.max() == 1) { 538 for (int i = 0; i < pe.min(); i++) { 539 Base b = null; 540 if (pe.schemaName().endsWith("[x]")) { 541 if (pe.types().size() == 1) { 542 b = base.addChild(pe.schemaName().replace("[x]", Utilities.capitalize(pe.types().get(0).getType()))); 543 } 544 } else if (!pe.isBaseList()) { 545 b = base.makeProperty(pe.schemaName().hashCode(), pe.schemaName()); 546 } else { 547 b = base.addChild(pe.schemaName()); 548 } 549 if (b != null) { 550 populateByProfile(b, pe); 551 } 552 } 553 } 554 } 555 } 556 } 557 558 public String makeSliceExpression(StructureDefinition profile, ElementDefinitionSlicingComponent slicing, ElementDefinition definition) { 559 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(" and "); 560 for (ElementDefinitionSlicingDiscriminatorComponent d : slicing.getDiscriminator()) { 561 switch (d.getType()) { 562 case EXISTS: 563 throw new DefinitionException("The discriminator type 'exists' is not supported by the PEBuilder"); 564 case PATTERN: 565 throw new DefinitionException("The discriminator type 'pattern' is not supported by the PEBuilder"); 566 case PROFILE: 567 throw new DefinitionException("The discriminator type 'profile' is not supported by the PEBuilder"); 568 case TYPE: 569 throw new DefinitionException("The discriminator type 'type' is not supported by the PEBuilder"); 570 case VALUE: 571 String path = d.getPath(); 572 if (path.contains(".")) { 573 throw new DefinitionException("The discriminator path '"+path+"' is not supported by the PEBuilder"); 574 } 575 ElementDefinition ed = getChildElement(profile, definition, path); 576 if (ed == null) { 577 throw new DefinitionException("The discriminator path '"+path+"' could not be resolved by the PEBuilder"); 578 } 579 if (!ed.hasFixed()) { 580 throw new DefinitionException("The discriminator path '"+path+"' has no fixed value - this is not supported by the PEBuilder"); 581 } 582 if (!ed.getFixed().isPrimitive()) { 583 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"); 584 } 585 b.append(path+" = '"+ed.getFixed().primitiveValue()+"'"); 586 break; 587 case NULL: 588 throw new DefinitionException("The discriminator type 'null' is not supported by the PEBuilder"); 589 default: 590 throw new DefinitionException("The discriminator type '??' is not supported by the PEBuilder"); 591 } 592 } 593 return b.toString(); 594 } 595 596 private ElementDefinition getChildElement(StructureDefinition profile, ElementDefinition definition, String path) { 597 List<ElementDefinition> elements = pu.getChildList(profile, definition); 598 if (elements.size() == 0) { 599 profile = definition.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, definition.getTypeFirstRep().getProfile().get(0).asStringValue()) : 600 context.fetchTypeDefinition(definition.getTypeFirstRep().getWorkingCode()); 601 elements = pu.getChildList(profile, profile.getSnapshot().getElementFirstRep()); 602 } 603 return getByName(elements, path); 604 } 605 606 public List<Base> exec(Resource resource, Base data, String fhirpath) { 607 return fpe.evaluate(this, resource, resource, data, fhirpath); 608 } 609 610 public boolean isResource(String name) { 611 return context.getResourceNamesAsSet().contains(name); 612 } 613}