
001package org.hl7.fhir.r5.renderers; 002 003import java.io.ByteArrayOutputStream; 004import java.io.IOException; 005import java.io.UnsupportedEncodingException; 006import java.util.ArrayList; 007import java.util.HashMap; 008import java.util.HashSet; 009import java.util.List; 010import java.util.Map; 011import java.util.Set; 012import java.util.Stack; 013 014import lombok.extern.slf4j.Slf4j; 015import org.apache.commons.lang3.StringUtils; 016import org.hl7.fhir.exceptions.DefinitionException; 017import org.hl7.fhir.exceptions.FHIRException; 018import org.hl7.fhir.exceptions.FHIRFormatError; 019import org.hl7.fhir.r5.comparison.VersionComparisonAnnotation; 020import org.hl7.fhir.r5.conformance.profile.BindingResolution; 021import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 022import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ElementChoiceGroup; 023import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ExtensionContext; 024import org.hl7.fhir.r5.conformance.profile.SnapshotGenerationPreProcessor; 025import org.hl7.fhir.r5.context.ExpansionOptions; 026import org.hl7.fhir.r5.extensions.ExtensionDefinitions; 027import org.hl7.fhir.r5.extensions.ExtensionUtilities; 028import org.hl7.fhir.r5.formats.IParser; 029import org.hl7.fhir.r5.formats.IParser.OutputStyle; 030import org.hl7.fhir.r5.formats.JsonParser; 031import org.hl7.fhir.r5.formats.XmlParser; 032import org.hl7.fhir.r5.model.ActorDefinition; 033import org.hl7.fhir.r5.model.Base; 034import org.hl7.fhir.r5.model.BooleanType; 035import org.hl7.fhir.r5.model.CanonicalResource; 036import org.hl7.fhir.r5.model.CanonicalType; 037import org.hl7.fhir.r5.model.CodeSystem; 038import org.hl7.fhir.r5.model.CodeType; 039import org.hl7.fhir.r5.model.CodeableConcept; 040import org.hl7.fhir.r5.model.Coding; 041import org.hl7.fhir.r5.model.ConceptMap; 042import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; 043import org.hl7.fhir.r5.model.DataType; 044import org.hl7.fhir.r5.model.DecimalType; 045import org.hl7.fhir.r5.model.Element; 046import org.hl7.fhir.r5.model.ElementDefinition; 047import org.hl7.fhir.r5.model.ElementDefinition.AdditionalBindingPurposeVS; 048import org.hl7.fhir.r5.model.ElementDefinition.AggregationMode; 049import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; 050import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; 051import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; 052import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; 053import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionExampleComponent; 054import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; 055import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent; 056import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; 057import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; 058import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; 059import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 060import org.hl7.fhir.r5.model.Enumeration; 061import org.hl7.fhir.r5.model.Extension; 062import org.hl7.fhir.r5.model.IdType; 063import org.hl7.fhir.r5.model.IntegerType; 064import org.hl7.fhir.r5.model.MarkdownType; 065import org.hl7.fhir.r5.model.PrimitiveType; 066import org.hl7.fhir.r5.model.Quantity; 067import org.hl7.fhir.r5.model.Resource; 068import org.hl7.fhir.r5.model.StringType; 069import org.hl7.fhir.r5.model.StructureDefinition; 070import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 071import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent; 072import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; 073import org.hl7.fhir.r5.model.UriType; 074import org.hl7.fhir.r5.model.ValueSet; 075import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; 076import org.hl7.fhir.r5.renderers.mappings.ConceptMapMappingProvider; 077import org.hl7.fhir.r5.renderers.mappings.ModelMappingProvider; 078import org.hl7.fhir.r5.renderers.mappings.StructureDefinitionMappingProvider; 079import org.hl7.fhir.r5.renderers.utils.ElementTable; 080import org.hl7.fhir.r5.renderers.utils.ElementTable.ElementTableGrouping; 081import org.hl7.fhir.r5.renderers.utils.ElementTable.ElementTableGroupingEngine; 082import org.hl7.fhir.r5.renderers.utils.ElementTable.ElementTableGroupingState; 083import org.hl7.fhir.r5.renderers.utils.ElementTable.HintDrivenGroupingEngine; 084import org.hl7.fhir.r5.renderers.utils.ElementTable.JsonDrivenGroupingEngine; 085import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElement; 086import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElementConstraint; 087import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElementConstraintType; 088import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElementDefinition; 089import org.hl7.fhir.r5.renderers.utils.ElementTable.TableElementDefinitionType; 090import org.hl7.fhir.r5.renderers.utils.ElementTable.TableGroup; 091import org.hl7.fhir.r5.renderers.utils.RenderingContext; 092import org.hl7.fhir.r5.renderers.utils.RenderingContext.FixedValueFormat; 093import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 094import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; 095import org.hl7.fhir.r5.renderers.utils.RenderingContext.StructureDefinitionRendererMode; 096import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 097import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; 098import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; 099import org.hl7.fhir.r5.utils.EOperationOutcome; 100import org.hl7.fhir.r5.utils.PublicationHacker; 101 102import org.hl7.fhir.r5.utils.UserDataNames; 103import org.hl7.fhir.utilities.*; 104import org.hl7.fhir.utilities.i18n.I18nConstants; 105import org.hl7.fhir.utilities.i18n.RenderingI18nContext; 106import org.hl7.fhir.utilities.json.JsonException; 107import org.hl7.fhir.utilities.json.model.JsonObject; 108import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; 109import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; 110import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; 111import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; 112import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableGenerationMode; 113import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; 114import org.hl7.fhir.utilities.xhtml.NodeType; 115import org.hl7.fhir.utilities.xhtml.XhtmlNode; 116import org.hl7.fhir.utilities.xhtml.XhtmlParser; 117 118@MarkedToMoveToAdjunctPackage 119@Slf4j 120public class StructureDefinitionRenderer extends ResourceRenderer { 121 122 public enum MapStructureMode { 123 IN_LIST, NOT_IN_LIST, OTHER 124 125 } 126 127 public StructureDefinitionRenderer(RenderingContext context) { 128 super(context); 129 hostMd = new InternalMarkdownProcessor(); 130 corePath = context.getContext().getSpecUrl(); 131 } 132 133 @Override 134 protected boolean willRenderId(ResourceWrapper r) { 135 if (r.isDirect()) { 136 StructureDefinition sd = (StructureDefinition) r.getBase(); 137 return sd.getIdBase().equals(sd.getSnapshot().getElementFirstRep().getPath()); 138 } 139 return false; 140 } 141 142 143 @Override 144 public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { 145 if (!r.isDirect()) { 146 // it seems very unlikely that this will change in the future 147 x.para().tx("StructureDefinitionRenderer only renders native resources directly"); 148 } else { 149 renderResourceTechDetails(r, x); 150 StructureDefinition sd = (StructureDefinition) r.getBase(); 151 genSummaryTable(status, x, sd); 152 if (context.getStructureMode() == StructureDefinitionRendererMode.DATA_DICT) { 153 renderDict(status, sd, sd.getDifferential().getElement(), x.table("dict", false).markGenerated(!context.forValidResource()), false, GEN_MODE_DIFF, "", r); 154 } else { 155 XhtmlNode node = generateTable(status, context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, 156 context.getLink(KnownLinkType.SPEC, false), "", sd.getKind() == StructureDefinitionKind.LOGICAL, false, null, false, context.withUniqueLocalPrefix(null), "r", r, "X"); 157 if (node == null) { 158// log("This shouldn't happen?"); 159 } else { 160 x.addChildNode(node); 161 } 162 } 163 status.setExtensions(true); 164 } 165 } 166 167 @Override 168 public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException { 169 return canonicalTitle(r); 170 } 171 172 public static class SourcedElementDefinition { 173 private StructureDefinition profile; 174 private ElementDefinition definition; 175 176 177 public SourcedElementDefinition(StructureDefinition profile, ElementDefinition definition) { 178 super(); 179 this.profile = profile; 180 this.definition = definition; 181 } 182 public StructureDefinition getProfile() { 183 return profile; 184 } 185 public ElementDefinition getDefinition() { 186 return definition; 187 } 188 189 } 190 191 public class InternalMarkdownProcessor implements IMarkdownProcessor { 192 193 @Override 194 public String processMarkdown(String location, PrimitiveType md) throws FHIRException { 195 return context.getMarkdown().process(md.primitiveValue(), location); 196 } 197 198 @Override 199 public String processMarkdown(String location, String text) throws FHIRException { 200 return context.getMarkdown().process(text, location); 201 } 202 } 203 204 private enum ListItemStatus { New, Unchanged, Removed} 205 206 private abstract class ItemWithStatus { 207 ListItemStatus status = ListItemStatus.New; // new, unchanged, removed 208 209 protected abstract void renderDetails(XhtmlNode f) throws IOException; 210 protected abstract boolean matches(ItemWithStatus other); 211 212 public void render(XhtmlNode x) throws IOException { 213 XhtmlNode f = x; 214 if (status == ListItemStatus.Unchanged) { 215 f = unchanged(f); 216 } else if (status == ListItemStatus.Removed) { 217 f = removed(f); 218 } 219 renderDetails(f); 220 } 221 } 222 223 protected class StatusList<T extends ItemWithStatus> extends ArrayList<T> implements List<T> { 224 225 public boolean merge(T item) { 226 if (item == null) { 227 return false; 228 } 229 boolean found = false; 230 for (T t : this) { 231 if (t.matches(item)) { 232 found = true; 233 t.status = ListItemStatus.Unchanged; 234 } 235 } 236 if (!found) { 237 item.status = ListItemStatus.Removed; 238 return add(item); 239 } else { 240 return false; 241 } 242 } 243 244 @Override 245 public boolean add(T item) { 246 if (item != null) { 247 return super.add(item); 248 } else { 249 return false; 250 } 251 } 252 } 253 254 private class ResolvedCanonical extends ItemWithStatus { 255 String url; // what we used to resolve 256 CanonicalResource cr; // what we resolved 257 258 public ResolvedCanonical(String url, CanonicalResource cr) { 259 this.url = url; 260 this.cr = cr; 261 } 262 public void renderDetails(XhtmlNode f) { 263 if (cr != null && cr.hasWebPath()) { 264 f.ah(context.prefixLocalHref(cr.getWebPath())).tx(cr.present()); 265 } else { 266 f.code().tx(url); 267 } 268 } 269 protected boolean matches(ItemWithStatus other) { 270 return ((ResolvedCanonical) other).url.equals(url); 271 } 272 } 273 274 private class InvariantWithStatus extends ItemWithStatus { 275 ElementDefinitionConstraintComponent value; 276 protected InvariantWithStatus(ElementDefinitionConstraintComponent value) { 277 this.value = value; 278 } 279 280 protected boolean matches(ItemWithStatus other) { 281 return ((InvariantWithStatus) other).value.equalsDeep(value); 282 } 283 284 public void renderDetails(XhtmlNode f) { 285 f = renderStatus(value, f); 286 f.b().attribute("title", context.formatPhrase(RenderingContext.STRUC_DEF_FII)).tx(value.getKey()); 287 f.tx(": "); 288 if (value.hasHuman()) { 289 renderStatus(value.getHumanElement(), f).tx(value.getHuman()); 290 } else if (VersionComparisonAnnotation.hasDeleted(value, "human")) { 291 Base b =VersionComparisonAnnotation.getDeletedItem(value, "human"); 292 renderStatus(b, f).tx(b.primitiveValue()); 293 } 294 f.tx(" ("); 295 if (status == ListItemStatus.New) { 296 if (value.hasExpression()) { 297 renderStatus(value.getExpressionElement(), f).code().tx(value.getExpression()); 298 } else if (VersionComparisonAnnotation.hasDeleted(value, "expression")) { 299 Base b = VersionComparisonAnnotation.getDeletedItem(value, "expression"); 300 renderStatus(b, f).code().tx(b.primitiveValue()); 301 } 302 } else { 303 renderStatus(value.getExpressionElement(), f).tx(value.getExpression()); 304 } 305 f.tx(")"); 306 } 307 } 308 309 private class DiscriminatorWithStatus extends ItemWithStatus { 310 ElementDefinitionSlicingDiscriminatorComponent value; 311 protected DiscriminatorWithStatus(ElementDefinitionSlicingDiscriminatorComponent value) { 312 this.value = value; 313 } 314 315 protected boolean matches(ItemWithStatus other) { 316 return ((DiscriminatorWithStatus) other).value.equalsDeep(value); 317 } 318 319 public void renderDetails(XhtmlNode f) { 320 f.tx(value.getType().toCode()); 321 f.tx(" @ "); 322 f.tx(value.getPath()); 323 } 324 } 325 326 private class ValueWithStatus extends ItemWithStatus { 327 PrimitiveType value; 328 protected ValueWithStatus(PrimitiveType value) { 329 this.value = value; 330 } 331 332 protected boolean matches(ItemWithStatus other) { 333 return ((ValueWithStatus) other).value.equalsDeep(value); 334 } 335 336 public void renderDetails(XhtmlNode f) { 337 if (value.hasUserData(UserDataNames.render_link)) { 338 f = f.ah(context.prefixLocalHref(value.getUserString(UserDataNames.render_link))); 339 } 340 f.tx(value.asStringValue()); 341 } 342 343 } 344 345 private class DataValueWithStatus extends ItemWithStatus { 346 DataType value; 347 private final DataRenderer renderer; 348 private final RenderingStatus status; 349 350 351 protected DataValueWithStatus(DataType value, DataRenderer renderer, RenderingStatus status) { 352 this.value = value; 353 this.renderer = renderer; 354 this.status = status; 355 } 356 357 protected boolean matches(ItemWithStatus other) { 358 return ((ValueWithStatus) other).value.equalsDeep(value); 359 } 360 361 public void renderDetails(XhtmlNode f) throws IOException { 362 363 if (value.hasUserData(UserDataNames.render_link)) { 364 f = f.ah(context.prefixLocalHref(value.getUserString(UserDataNames.render_link))); 365 } 366 ResourceWrapper v = ResourceWrapper.forType(renderer.context.getContextUtilities(), value); 367 renderer.renderDataType(status, f, v); 368 } 369 370 } 371 372 373 private List<String> keyRows = new ArrayList<>(); 374 private Map<String, Map<String, ElementDefinition>> sdMapCache = new HashMap<>(); 375 private IMarkdownProcessor hostMd; 376 private Map<String, Integer> anchors = new HashMap<>(); 377 378 379 public Map<String, Map<String, ElementDefinition>> getSdMapCache() { 380 return sdMapCache; 381 } 382 383 public void setSdMapCache(Map<String, Map<String, ElementDefinition>> sdMapCache) { 384 this.sdMapCache = sdMapCache; 385 } 386 387 public IMarkdownProcessor getHostMd() { 388 return hostMd; 389 } 390 391 public void setHostMd(IMarkdownProcessor hostMd) { 392 this.hostMd = hostMd; 393 } 394 395 396 397 public void describe(XhtmlNode x, StructureDefinition sd) { 398 x.tx(display(sd)); 399 } 400 401 public String display(StructureDefinition sd) { 402 return sd.present(); 403 } 404 405 // private static final int AGG_NONE = 0; 406 // private static final int AGG_IND = 1; 407 // private static final int AGG_GR = 2; 408 // private static final boolean TABLE_FORMAT_FOR_FIXED_VALUES = false; 409 public static final String CONSTRAINT_CHAR = "C"; 410 public static final String CONSTRAINT_STYLE = "padding-left: 3px; padding-right: 3px; border: 1px maroon solid; font-weight: bold; color: #301212; background-color: #fdf4f4;"; 411 public static final int GEN_MODE_SNAP = 1; 412 public static final int GEN_MODE_DIFF = 2; 413 public static final int GEN_MODE_MS = 3; 414 public static final int GEN_MODE_KEY = 4; 415 public static final String RIM_MAPPING = "http://hl7.org/v3"; 416 public static final String v2_MAPPING = "http://hl7.org/v2"; 417 public static final String LOINC_MAPPING = "http://loinc.org"; 418 public static final String SNOMED_MAPPING = "http://snomed.info"; 419 private static final boolean PREFIX_SLICES = true; 420 421 private final boolean ADD_REFERENCE_TO_TABLE = true; 422 423 private boolean useTableForFixedValues = true; 424 private String corePath; 425 private JsonObject resourceGroupings; 426 private MapStructureMode mappingsMode; 427 private List<StructureDefinition> mappingTargets = new ArrayList<>(); 428 429 public static class UnusedTracker { 430 private boolean used; 431 } 432 433 private class SpanEntry { 434 private List<SpanEntry> children = new ArrayList<SpanEntry>(); 435 private boolean profile; 436 private String id; 437 private String name; 438 private String resType; 439 private String cardinality; 440 private String description; 441 private String profileLink; 442 private String resLink; 443 private String type; 444 445 public String getName() { 446 return name; 447 } 448 public void setName(String name) { 449 this.name = name; 450 } 451 public String getResType() { 452 return resType; 453 } 454 public void setResType(String resType) { 455 this.resType = resType; 456 } 457 public String getCardinality() { 458 return cardinality; 459 } 460 public void setCardinality(String cardinality) { 461 this.cardinality = cardinality; 462 } 463 public String getDescription() { 464 return description; 465 } 466 public void setDescription(String description) { 467 this.description = description; 468 } 469 public String getProfileLink() { 470 return profileLink; 471 } 472 public void setProfileLink(String profileLink) { 473 this.profileLink = profileLink; 474 } 475 public String getResLink() { 476 return resLink; 477 } 478 public void setResLink(String resLink) { 479 this.resLink = resLink; 480 } 481 public String getId() { 482 return id; 483 } 484 public void setId(String id) { 485 this.id = id; 486 } 487 public boolean isProfile() { 488 return profile; 489 } 490 public void setProfile(boolean profile) { 491 this.profile = profile; 492 } 493 public List<SpanEntry> getChildren() { 494 return children; 495 } 496 public String getType() { 497 return type; 498 } 499 public void setType(String type) { 500 this.type = type; 501 } 502 503 } 504 505 private class ElementInStructure { 506 507 private StructureDefinition source; 508 private ElementDefinition element; 509 510 public ElementInStructure(StructureDefinition source, ElementDefinition ed) { 511 this.source = source; 512 this.element = ed; 513 } 514 515 public StructureDefinition getSource() { 516 return source; 517 } 518 519 public ElementDefinition getElement() { 520 return element; 521 } 522 523 } 524 private ElementInStructure getElementByName(List<ElementDefinition> elements, String contentReference, StructureDefinition source) { 525 if (contentReference.contains("#")) { 526 String url = contentReference.substring(0, contentReference.indexOf("#")); 527 contentReference = contentReference.substring(contentReference.indexOf("#")); 528 if (Utilities.noString(url)) { 529 url = source.getUrl(); 530 } 531 if (!url.equals(source.getUrl())) { 532 source = context.getProfileUtilities().findProfile(url, source); 533 if (source == null) { 534 throw new FHIRException(context.formatPhrase(RenderingContext.STRUC_DEF_REND_UNABLE_RES, url, contentReference)); 535 } 536 elements = source.getSnapshot().getElement(); 537 } 538 } 539 for (ElementDefinition ed : elements) { 540 if (("#"+ed.getPath()).equals(contentReference)) { 541 return new ElementInStructure(source, ed); 542 } 543 if (("#"+ed.getId()).equals(contentReference)) { 544 return new ElementInStructure(source, ed); 545 } 546 } 547 throw new Error(context.formatPhrase(RenderingContext.STRUC_DEF_CANT_FIND, contentReference, elements.toString(), source.getUrl())); 548 // return null; 549 } 550 551 public XhtmlNode generateGrid(String defFile, StructureDefinition profile, String imageFolder, boolean inlineGraphics, String profileBaseFileName, String corePath, String imagePath, Set<String> outputTracker, boolean diff) throws IOException, FHIRException { 552 anchors.clear(); 553 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true, "g"); 554 TableModel model = gen.initGridTable(corePath, profile.getId()); 555 List<ElementDefinition> list = profile.getSnapshot().getElement(); 556 List<StructureDefinition> profiles = new ArrayList<StructureDefinition>(); 557 profiles.add(profile); 558 genGridElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, true, profileBaseFileName, null, corePath, imagePath, true, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), diff); 559 try { 560 return gen.generate(model, imagePath, 1, outputTracker); 561 } catch (org.hl7.fhir.exceptions.FHIRException e) { 562 throw new FHIRException(e.getMessage(), e); 563 } 564 } 565 566 567 public static class Column { 568 String id; 569 String title; 570 String hint; 571 private String link; 572 573 public Column(String id, String title, String hint) { 574 super(); 575 this.id = id; 576 this.title = title; 577 this.hint = hint; 578 } 579 public Column(String id, String title, String hint, String link) { 580 super(); 581 this.id = id; 582 this.title = title; 583 this.hint = hint; 584 this.link = link; 585 } 586 587 } 588 589 public XhtmlNode generateTable(RenderingStatus status, String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, 590 boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean mustSupport, RenderingContext rc, String anchorPrefix, ResourceWrapper res, String idSfx) throws IOException, FHIRException { 591 assert(diff != snapshot);// check it's ok to get rid of one of these 592 anchors.clear(); 593 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true, defFile, rc.getUniqueLocalPrefix()); 594 595 TableModel model = generateTableInner(status, defFile, profile, diff, profileBaseFileName, snapshot, corePath, imagePath, logicalModel, allInvariants, mustSupport, rc, anchorPrefix, res, idSfx, gen); 596 if (model == null) return null; 597 try { 598 return gen.generate(model, imagePath, 0, outputTracker); 599 } catch (org.hl7.fhir.exceptions.FHIRException e) { 600 throw new FHIRException(context.getWorker().formatMessage(I18nConstants.ERROR_GENERATING_TABLE_FOR_PROFILE__, profile.getUrl(), e.getMessage()), e); 601 } 602 } 603 604 public XhtmlNode generateAttributeTable(RenderingStatus status, String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, 605 boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean mustSupport, RenderingContext rc, String anchorPrefix, ResourceWrapper res, String idSfx) throws IOException, FHIRException { 606 assert(diff != snapshot);// check it's ok to get rid of one of these 607 anchors.clear(); 608 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true, defFile, rc.getUniqueLocalPrefix()); 609 610 TableModel model = generateTableInner(status, defFile, profile, diff, profileBaseFileName, snapshot, corePath, imagePath, logicalModel, allInvariants, mustSupport, rc, anchorPrefix, res, idSfx, gen); 611 if (model == null) { 612 return null; 613 } 614 try { 615 return gen.generateAttributeTable(model, imagePath, 0, outputTracker); 616 } catch (org.hl7.fhir.exceptions.FHIRException e) { 617 throw new FHIRException(context.getWorker().formatMessage(I18nConstants.ERROR_GENERATING_TABLE_FOR_PROFILE__, profile.getUrl(), e.getMessage()), e); 618 } 619 } 620 621 private TableModel generateTableInner(RenderingStatus status, String defFile, StructureDefinition profile, boolean diff, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, boolean logicalModel, boolean allInvariants, boolean mustSupport, RenderingContext rc, String anchorPrefix, ResourceWrapper res, String idSfx, HierarchicalTableGenerator gen) throws IOException { 622 List<ElementDefinition> list; 623 if (diff) { 624 list = new SnapshotGenerationPreProcessor(context.getProfileUtilities()).supplementMissingDiffElements(profile); 625 } else { 626 list = new ArrayList<>(); 627 list.addAll(profile.getSnapshot().getElement()); 628 } 629 630 List<Column> columns = new ArrayList<>(); 631 List<ModelMappingProvider> mappings = new ArrayList<>(); 632 TableModel model; 633 boolean obLists = false; 634 switch (context.getStructureMode()) { 635 case BINDINGS: 636 scanBindings(columns, list); 637 model = initCustomTable(gen, corePath, false, true, profile.getId()+ idSfx, rc.getRules() == GenerationRules.IG_PUBLISHER, columns); 638 break; 639 case OBLIGATIONS: 640 obLists = scanObligations(columns, list); 641 model = initCustomTable(gen, corePath, false, true, profile.getId()+ idSfx, rc.getRules() == GenerationRules.IG_PUBLISHER, columns); 642 break; 643 case MAPPINGS: 644 mappings = scanForMappings(profile, list, columns); 645 if (mappings.isEmpty()) { 646 return null; 647 } 648 model = initCustomTable(gen, corePath, false, true, profile.getId()+ idSfx, rc.getRules() == GenerationRules.IG_PUBLISHER, columns); 649 break; 650 case SUMMARY: 651 model = gen.initNormalTable(corePath, false, true, profile.getId()+ idSfx, rc.getRules() == GenerationRules.IG_PUBLISHER, rc.getRules() == GenerationRules.IG_PUBLISHER ? TableGenerationMode.XHTML : TableGenerationMode.XML); 652 break; 653 default: 654 throw new Error(context.formatPhrase(RenderingContext.STRUC_DEF_ERROR)); 655 } 656 657 List<StructureDefinition> profiles = new ArrayList<StructureDefinition>(); 658 profiles.add(profile); 659 keyRows.clear(); 660 661 genElement(status, defFile == null ? null : defFile +"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null, mustSupport, rc, anchorPrefix, profile, columns, res, obLists, mappings, diff); 662 return model; 663 } 664 665 private List<ModelMappingProvider> scanForMappings(StructureDefinition profile, List<ElementDefinition> list, List<Column> columns) { 666 List<ModelMappingProvider> res = new ArrayList<ModelMappingProvider>(); 667 List<StructureDefinition> items = new ArrayList<StructureDefinition>(); 668 669 // first, does this have mappings to other models? 670 for (StructureDefinitionMappingComponent map : profile.getMapping()) { 671 if (map.hasUri()) { 672 StructureDefinition sd = context.getProfileUtilities().findProfile(map.getUri(), profile); 673 if (includeSDForMap(sd, true)) { 674 items.add(sd); 675 res.add(new StructureDefinitionMappingProvider(context, sd, false, profile, map, context.getProfileUtilities().getFpe())); 676 } 677 } 678 } 679 for (ConceptMap cm : context.getContext().fetchResourcesByType(ConceptMap.class)) { 680 for (ConceptMapGroupComponent grp : cm.getGroup()) { 681 if (grp.hasSource() && grp.getSource().equals(ProfileUtilities.getCSUrl(profile))) { 682 boolean matched = true; 683 String url = ProfileUtilities.getUrlFromCSUrl(grp.getTarget()); 684 if (url != null) { 685 StructureDefinition sd = context.getProfileUtilities().findProfile(url, profile); 686 if (includeSDForMap(sd, false) && !items.contains(sd)) { 687 matched = true; 688 items.add(sd); 689 res.add(new ConceptMapMappingProvider(context, sd, false, cm, grp)); 690 } 691 } 692 if (!matched) { 693 for (StructureDefinition sd : context.getContextUtilities().allStructures()) { 694 if (includeSDForMap(sd, false)) { 695 String url2 = ProfileUtilities.getCSUrl(sd); 696 if (url2.equals(grp.getTarget()) && !items.contains(sd)) { 697 items.add(sd); 698 res.add(new ConceptMapMappingProvider(context, sd, false, cm, grp)); 699 break; 700 } 701 } 702 } 703 } 704 } 705 } 706 } 707 708 // now look for reverse mappings but only to things we haven't already got forward mappings too 709 for (StructureDefinition src : context.getContextUtilities().allStructures()) { 710 if (includeSDForMap(src, true)) { 711 for (StructureDefinitionMappingComponent map : src.getMapping()) { 712 if (map.hasUri() && map.getUri().equals(profile.getUrl()) && !items.contains(src)) { 713 items.add(src); 714 res.add(new StructureDefinitionMappingProvider(context, src, true, profile, map, context.getProfileUtilities().getFpe())); 715 } 716 } 717 } 718 } 719 720 for (ConceptMap cm : context.getContext().fetchResourcesByType(ConceptMap.class)) { 721 for (ConceptMapGroupComponent grp : cm.getGroup()) { 722 if (grp.hasTarget() && grp.getTarget().equals(ProfileUtilities.getCSUrl(profile))) { 723 boolean matched = true; 724 String url = ProfileUtilities.getUrlFromCSUrl(grp.getSource()); 725 if (url != null) { 726 StructureDefinition sd = context.getProfileUtilities().findProfile(url, profile); 727 if (includeSDForMap(sd, false) && !items.contains(sd)) { 728 matched = true; 729 items.add(sd); 730 res.add(new ConceptMapMappingProvider(context, sd, true, cm, grp)); 731 } 732 } 733 if (!matched) { 734 for (StructureDefinition sd : context.getContextUtilities().allStructures()) { 735 if (includeSDForMap(sd, false)) { 736 String url2 = ProfileUtilities.getCSUrl(sd); 737 if (url2.equals(grp.getSource()) && !items.contains(sd)) { 738 items.add(sd); 739 res.add(new ConceptMapMappingProvider(context, sd, true, cm, grp)); 740 break; 741 } 742 } 743 } 744 } 745 } 746 } 747 } 748 int i = 0; 749 for (ModelMappingProvider mm : res) { 750 columns.add(mm.makeColumn("m"+ ++i)); 751 } 752 profile.setUserData(UserDataNames.PROFILE_RENDERED_MAPPINGS, res.size() >= 0); 753 return res; 754 } 755 756 private boolean includeSDForMap(StructureDefinition sd, boolean okForNull) { 757 if (mappingsMode == null) { 758 return false; 759 } 760 switch (mappingsMode) { 761 case IN_LIST: return mappingTargets.contains(sd); 762 case NOT_IN_LIST: return sd != null && !mappingTargets.contains(sd); 763 case OTHER: return sd == null && okForNull; 764 default: return false; 765 } 766 } 767 768 private void scanBindings(List<Column> columns, List<ElementDefinition> list) { 769 Set<String> cols = new HashSet<>(); 770 scanBindings(cols, list, list.get(0)); 771 if (cols.contains("required")) { 772 columns.add(new Column("required", context.formatPhrase(RenderingContext.GENERAL_REQUIRED), context.formatPhrase(RenderingContext.STRUC_DEF_CONC_SET))); 773 } 774 if (cols.contains("extensible")) { 775 columns.add(new Column("extensible", context.formatPhrase(RenderingContext.STRUC_DEF_EXT), context.formatPhrase(RenderingContext.STRUC_DEF_APPROP_CON))); 776 } 777 if (cols.contains("maximum")) { 778 columns.add(new Column("maximum", context.formatPhrase(RenderingContext.STRUC_DEF_MAX), context.formatPhrase(RenderingContext.STRUC_DEF_REQ_BIND))); 779 } 780 if (cols.contains("minimum")) { 781 columns.add(new Column("minimum", context.formatPhrase(RenderingContext.STRUC_DEF_MIN), context.formatPhrase(RenderingContext.GENERAL_BIND_MIN_ALLOW))); 782 } 783 if (cols.contains("candidate")) { 784 columns.add(new Column("candidate", context.formatPhrase(RenderingContext.STRUC_DEF_CAND), context.formatPhrase(RenderingContext.STRUC_DEF_CAND_SUB))); 785 } 786 if (cols.contains("current")) { 787 columns.add(new Column("current", context.formatPhrase(RenderingContext.STRUC_DEF_CURR), context.formatPhrase(RenderingContext.STRUC_DEF_CURR_RULE))); 788 } 789 if (cols.contains("preferred")) { 790 columns.add(new Column("preferred", context.formatPhrase(RenderingContext.GENERAL_PREFERRED), context.formatPhrase(RenderingContext.STRUC_DEF_PREF_CONT))); 791 } 792 if (cols.contains("ui")) { 793 columns.add(new Column("ui", context.formatPhrase(RenderingContext.STRUC_DEF_UI), context.formatPhrase(RenderingContext.STRUC_DEF_UI_CONT))); 794 } 795 if (cols.contains("starter")) { 796 columns.add(new Column("starter", context.formatPhrase(RenderingContext.GENERAL_STARTER), context.formatPhrase(RenderingContext.ADD_BIND_DESIG_SYS))); 797 } 798 if (cols.contains("component")) { 799 columns.add(new Column("component", context.formatPhrase(RenderingContext.GENERAL_COMPONENT), context.formatPhrase(RenderingContext.STRUC_DEF_COMP_DOC))); 800 } 801 if (cols.contains("example")) { 802 columns.add(new Column("example", context.formatPhrase(RenderingContext.GENERAL_EXAMPLE), context.formatPhrase(RenderingContext.STRUC_DEF_EX_DESC))); 803 } 804 } 805 806 public void scanBindings(Set<String> cols, List<ElementDefinition> list, ElementDefinition ed) { 807 if (ed.hasBinding()) { 808 if (ed.getBinding().hasValueSet() && ed.getBinding().hasStrength()) { 809 switch (ed.getBinding().getStrength()) { 810 case EXAMPLE: 811 cols.add(context.formatPhrase(RenderingContext.STRUC_DEF_EXAM)); 812 break; 813 case EXTENSIBLE: 814 cols.add(context.formatPhrase(RenderingContext.STRUC_DEF_EXTENSIBLE)); 815 break; 816 case PREFERRED: 817 cols.add(context.formatPhrase(RenderingContext.STRUC_DEF_PREFERRED)); 818 break; 819 case REQUIRED: 820 cols.add(context.formatPhrase(RenderingContext.STRUC_DEF_REQUIRED)); 821 break; 822 default: 823 break; 824 } 825 } 826 for (ElementDefinitionBindingAdditionalComponent ab : ed.getBinding().getAdditional()) { 827 cols.add(ab.getPurpose().toCode()); 828 } 829 for (Extension ext : ed.getBinding().getExtensionsByUrl(ExtensionDefinitions.EXT_BINDING_ADDITIONAL)) { 830 cols.add(ext.getExtensionString("purpose")); 831 } 832 } 833 834 List<ElementDefinition> children = getChildren(list, ed); 835 for (ElementDefinition element : children) { 836 scanBindings(cols, list, element); 837 } 838 } 839 840 private boolean scanObligations(List<Column> columns, List<ElementDefinition> list) { 841 Set<String> cols = new HashSet<>(); 842 boolean res = scanObligations(cols, list, list.get(0)); 843 844 if (cols.contains("$all")) { 845 columns.add(new Column("$all", context.formatPhrase(RenderingContext.STRUC_DEF_ALL_ACTORS), context.formatPhrase(RenderingContext.STRUC_DEF_OBLIG_ALL))); 846 } 847 for (String col : cols) { 848 if (!"$all".equals(col)) { 849 ActorDefinition actor = context.getWorker().fetchResource(ActorDefinition.class, col); 850 if (actor == null) { 851 columns.add(new Column(col, urlTail(col), context.formatPhrase(RenderingContext.STRUC_DEF_UNDEF_ACT, col, col)+" ")); 852 } else { 853 columns.add(new Column(col, actor.present(), context.formatPhrase(RenderingContext.STRUC_DEF_ACT, actor.present(), actor.getWebPath())+" ")); 854 } 855 } 856 } 857 return res; 858 } 859 860 private String urlTail(String url) { 861 return url.contains("/") ? url.substring(url.lastIndexOf("/")+1) : url; 862 } 863 864 private boolean scanObligations(Set<String> cols, List<ElementDefinition> list, ElementDefinition ed) { 865 boolean res = true; 866 Map<String, Integer> nameCounts = new HashMap<>(); 867 for (Extension ob : ed.getExtensionsByUrl(ExtensionDefinitions.EXT_OBLIGATION_CORE, ExtensionDefinitions.EXT_OBLIGATION_TOOLS)) { 868 if (ob.hasExtension("actor", "actorId")) { 869 for (Extension a : ob.getExtensionsByUrl("actor", "actorId")) { 870 String name = a.getValueCanonicalType().primitiveValue(); 871 cols.add(name); 872 nameCounts.put(name, nameCounts.getOrDefault(name, 0) + 1); 873 } 874 } else { 875 cols.add("$all"); 876 nameCounts.put("$all", nameCounts.getOrDefault("$all", 0) + 1); 877 } 878 } 879 for (Map.Entry<String, Integer> entry : nameCounts.entrySet()) { 880 if (entry.getValue() > 1) { 881 res = false; 882 } 883 } 884 List<ElementDefinition> children = getChildren(list, ed); 885 for (ElementDefinition element : children) { 886 res = scanObligations(cols, list, element) && res; 887 } 888 return res; 889 } 890 891 public TableModel initCustomTable(HierarchicalTableGenerator gen, String prefix, boolean isLogical, boolean alternating, String id, boolean isActive, List<Column> columns) throws IOException { 892 TableModel model = gen.new TableModel(id, isActive); 893 894 model.setAlternating(alternating); 895 if (context.getRules() == GenerationRules.VALID_RESOURCE || context.isInlineGraphics()) { 896 model.setDocoImg(HierarchicalTableGenerator.help16AsData()); 897 } else { 898 model.setDocoImg(Utilities.pathURL(prefix, "help16.png")); 899 } 900 model.setDocoRef(Utilities.pathURL("https://build.fhir.org/ig/FHIR/ig-guidance", "readingIgs.html#table-views")); 901 model.getTitles().add(gen.new Title(null, model.getDocoRef(), (context.formatPhrase(RenderingContext.GENERAL_NAME)), (context.formatPhrase(RenderingContext.GENERAL_LOGICAL_NAME)), null, 0)); 902 for (Column col : columns) { 903 model.getTitles().add(gen.new Title(null, model.getDocoRef(), (col.title), (col.hint), null, 0)); 904 } 905 return model; 906 } 907 908 909 public TableModel initElementTable(HierarchicalTableGenerator gen, String prefix, boolean alternating, String id, boolean isActive, TableGenerationMode mode) throws IOException { 910 TableModel model = gen.new TableModel(id, isActive); 911 912 model.setAlternating(alternating); 913 if (mode == TableGenerationMode.XML) { 914 model.setDocoImg(HierarchicalTableGenerator.help16AsData()); 915 } else { 916 model.setDocoImg(Utilities.pathURL(prefix, "help16.png")); 917 } 918 model.setDocoRef(Utilities.pathURL("https://build.fhir.org/ig/FHIR/ig-guidance", "readingIgs.html#table-views")); 919 // these need to be defined, but they're never going to be shown 920 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingI18nContext.GENERAL_NAME), context.formatPhrase(RenderingI18nContext.GENERAL_LOGICAL_NAME), null, 0)); 921 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingI18nContext.GENERAL_DESC_CONST), context.formatPhrase(RenderingI18nContext.SD_HEAD_DESC_DESC), null, 0)); 922 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingI18nContext.GENERAL_TYPE), context.formatPhrase(RenderingI18nContext.SD_GRID_HEAD_TYPE_DESC), null, 0)); 923 return model; 924 } 925 926 private Row genElement(RenderingStatus status, String defPath, HierarchicalTableGenerator gen, List<Row> rows, ElementDefinition element, List<ElementDefinition> all, List<StructureDefinition> profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, 927 boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport, RenderingContext rc, String anchorPrefix, StructureDefinition srcSD, List<Column> columns, ResourceWrapper res, boolean obLists, List<ModelMappingProvider> mappings, boolean diff) throws IOException, FHIRException { 928 Row originalRow = slicingRow; 929 StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); 930 Row typesRow = null; 931 932 List<ElementDefinition> children = getChildren(all, element); 933 // if (!snapshot && isExtension && extensions != null && extensions != isExtension) 934 // return; 935 936 if (!(onlyInformationIsMapping(all, element) || (context.getStructureMode() == StructureDefinitionRendererMode.OBLIGATIONS && !elementOrDescendentsHaveObligations(all, element)))) { 937 Row row = gen.new Row(); 938 // in deeply sliced things, there can be duplicate paths that are not usefully differentiated by id, and anyway, we want path 939 String anchor = element.getPath(); 940 anchor = makeAnchorUnique(anchor); 941 row.setId(context.prefixAnchor(anchor)); 942 row.setAnchor(context.prefixAnchor(anchor)); 943 row.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); 944 if (element.hasSlicing()) 945 row.setLineColor(1); 946 else if (element.hasSliceName()) 947 row.setLineColor(2); 948 else 949 row.setLineColor(0); 950 boolean hasDef = element != null; 951 boolean ext = false; 952 if (tail(element.getPath()).equals("extension") && isExtension(element)) { 953 if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue(), srcSD)) 954 row.setIcon("icon_extension_complex.png", context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_COMPLEX)); 955 else 956 row.setIcon("icon_extension_simple.png", context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_SIMPLE)); 957 ext = true; 958 } else if (tail(element.getPath()).equals("modifierExtension")) { 959 if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue(), srcSD)) 960 row.setIcon("icon_modifier_extension_complex.png", context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_COMPLEX)); 961 else 962 row.setIcon("icon_modifier_extension_simple.png", context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_SIMPLE)); 963 ext = true; 964 } else if (!hasDef || element.getType().size() == 0) { 965 if (root && profile != null && context.getWorker().getResourceNames().contains(profile.getType())) { 966 row.setIcon("icon_resource.png", context.formatPhrase(RenderingContext.GENERAL_RESOURCE)); 967 } else if (hasDef && element.hasExtension(ExtensionDefinitions.EXT_JSON_PROP_KEY)) { 968 row.setIcon("icon-object-box.png", context.formatPhrase(RenderingContext.TEXT_ICON_OBJECT_BOX)); 969 keyRows.add(element.getId()+"."+ ExtensionUtilities.readStringExtension(element, ExtensionDefinitions.EXT_JSON_PROP_KEY)); 970 } else { 971 row.setIcon("icon_element.gif", context.formatPhrase(RenderingContext.TEXT_ICON_ELEMENT)); 972 } 973 } else if (hasDef && element.getType().size() > 1) { 974 if (allAreReference(element.getType())) { 975 row.setIcon("icon_reference.png", context.formatPhrase(RenderingContext.TEXT_ICON_REFERENCE)); 976 } else if (element.hasExtension(ExtensionDefinitions.EXT_JSON_PRIMITIVE_CHOICE)) { 977 row.setIcon("icon_choice.gif", context.formatPhrase(RenderingContext.TEXT_ICON_CHOICE)); 978 } else { 979 row.setIcon("icon_choice.gif", context.formatPhrase(RenderingContext.TEXT_ICON_CHOICE)); 980 typesRow = row; 981 } 982 } else if (hasDef && element.getType().get(0).getWorkingCode() != null && element.getType().get(0).getWorkingCode().startsWith("@")) { 983 row.setIcon("icon_reuse.png", context.formatPhrase(RenderingContext.TEXT_ICON_REUSE)); 984 } else if (hasDef && context.getContext().isPrimitiveType(element.getType().get(0).getWorkingCode())) { 985 if (keyRows.contains(element.getId())) { 986 row.setIcon("icon-key.png", context.formatPhrase(RenderingContext.TEXT_ICON_KEY)); 987 } else { 988 row.setIcon("icon_primitive.png", context.formatPhrase(RenderingContext.TEXT_ICON_PRIMITIVE)); 989 } 990 } else if (hasDef && element.getType().get(0).hasTarget()) { 991 row.setIcon("icon_reference.png", context.formatPhrase(RenderingContext.TEXT_ICON_REFERENCE)); 992 } else if (hasDef && context.getContext().isDataType(element.getType().get(0).getWorkingCode())) { 993 row.setIcon("icon_datatype.gif", context.formatPhrase(RenderingContext.TEXT_ICON_DATATYPE)); 994 } else if (hasDef && element.hasExtension(ExtensionDefinitions.EXT_JSON_PROP_KEY)) { 995 row.setIcon("icon-object-box.png", context.formatPhrase(RenderingContext.TEXT_ICON_OBJECT_BOX)); 996 keyRows.add(element.getId()+"."+ExtensionUtilities.readStringExtension(element, ExtensionDefinitions.EXT_JSON_PROP_KEY)); 997 } else if (hasDef && Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Base", "Element", "BackboneElement")) { 998 row.setIcon("icon_element.gif", context.formatPhrase(RenderingContext.TEXT_ICON_ELEMENT)); 999 } else { 1000 row.setIcon("icon_resource.png", context.formatPhrase(RenderingContext.GENERAL_RESOURCE)); 1001 } 1002 if (element.hasUserData(UserDataNames.render_opaque)) { 1003 row.setOpacity("0.5"); 1004 } 1005 UnusedTracker used = new UnusedTracker(); 1006 String ref = defPath == null ? null : defPath + element.getId(); 1007 String sName = null; 1008 if (PREFIX_SLICES) { 1009 sName = tail(element.getPath()); 1010 if (element.hasSliceName()) { 1011 sName = sName + ":" + element.getSliceName(); 1012 } 1013 } else { 1014 sName = element.hasSliceName() ? element.getSliceName() : tail(element.getPath()); 1015 } 1016 used.used = true; 1017 if (logicalModel) { 1018 if (element.hasRepresentation(PropertyRepresentation.XMLATTR)) { 1019 sName = "@"+sName; 1020 } else if (element.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER)) { 1021 ElementDefinition drv = (ElementDefinition) element.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER); 1022 if (drv.hasRepresentation(PropertyRepresentation.XMLATTR)) { 1023 sName = "@"+sName; 1024 } 1025 } 1026 } 1027 Cell nc = genElementNameCell(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, all); 1028 switch (context.getStructureMode()) { 1029 case BINDINGS: 1030 genElementBindings(gen, element, columns, row, profile, corePath); 1031 break; 1032 case OBLIGATIONS: 1033 genElementObligations(gen, element, columns, row, corePath, profile, obLists); 1034 break; 1035 case MAPPINGS: 1036 genElementMappings(gen, element, columns, row, corePath, profile, mappings); 1037 break; 1038 case SUMMARY: 1039 genElementCells(status, gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, nc, mustSupport, true, rc, children.size() > 0, defPath, anchorPrefix, all, res, diff); 1040 break; 1041 } 1042 if (element.hasSlicing()) { 1043 if (standardExtensionSlicing(element)) { 1044 used.used = true; // doesn't matter whether we have a type, we're used if we're setting up slicing ... element.hasType() && element.getType().get(0).hasProfile(); 1045 showMissing = false; //? 1046 slicingRow = row; 1047 } else { 1048 row.setIcon("icon_slice.png", context.formatPhrase(RenderingContext.TEXT_ICON_SLICE)); 1049 slicingRow = row; 1050 for (Cell cell : row.getCells()) 1051 for (Piece p : cell.getPieces()) { 1052 p.addStyle("font-style: italic"); 1053 } 1054 } 1055 } else if (element.hasSliceName()) { 1056 row.setIcon("icon_slice_item.png", context.formatPhrase(RenderingContext.TEXT_ICON_SLICE_ITEM)); 1057 } 1058 if (used.used || showMissing) 1059 rows.add(row); 1060 if (!used.used && !element.hasSlicing()) { 1061 for (Cell cell : row.getCells()) 1062 for (Piece p : cell.getPieces()) { 1063 if (p.isUnderived()) { 1064 p.setStyle("font-style: italic"); 1065 } else { 1066 p.setStyle("text-decoration:line-through"); 1067 } 1068 } 1069 } else { 1070 if (slicingRow != originalRow && !children.isEmpty()) { 1071 // we've entered a slice; we're going to create a holder row for the slice children 1072 Row hrow = gen.new Row(); 1073 String anchorE = makeAnchorUnique(element.getPath()); 1074 hrow.setId(context.prefixAnchor(anchorE)); 1075 hrow.setAnchor(context.prefixAnchor(anchorE)); 1076 hrow.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); 1077 hrow.setLineColor(1); 1078 hrow.setIcon("icon_element.gif", context.formatPhrase(RenderingContext.TEXT_ICON_ELEMENT)); 1079 hrow.getCells().add(gen.new Cell(null, null, sName+ (context.formatPhrase(RenderingContext.STRUC_DEF_ALL_SLICES)), "", null)); 1080 switch (context.getStructureMode()) { 1081 case BINDINGS: 1082 case OBLIGATIONS: 1083 case MAPPINGS: 1084 for (Column col : columns) { 1085 hrow.getCells().add(gen.new Cell()); 1086 } 1087 break; 1088 case SUMMARY: 1089 hrow.getCells().add(gen.new Cell()); 1090 hrow.getCells().add(gen.new Cell()); 1091 hrow.getCells().add(gen.new Cell()); 1092 hrow.getCells().add(gen.new Cell(null, null, context.formatPhrase(RenderingContext.STRUC_DEF_CONT_RULE), "", null)); 1093 break; 1094 } 1095 row.getSubRows().add(hrow); 1096 row = hrow; 1097 } 1098 if (typesRow != null && !children.isEmpty()) { 1099 // we've entered a typing slice; we're going to create a holder row for the all types children 1100 Row hrow = gen.new Row(); 1101 String anchorE = element.getPath(); 1102 anchorE = makeAnchorUnique(anchorE); 1103 hrow.setId(context.prefixAnchor(anchorE)); 1104 hrow.setAnchor(context.prefixAnchor(anchorE)); 1105 hrow.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); 1106 hrow.setLineColor(1); 1107 hrow.setIcon("icon_element.gif", context.formatPhrase(RenderingContext.TEXT_ICON_ELEMENT)); 1108 hrow.getCells().add(gen.new Cell(null, null, sName+ context.formatPhrase(RenderingContext.STRUC_DEF_ALL_TYPES), "", null)); 1109 switch (context.getStructureMode()) { 1110 case BINDINGS: 1111 case OBLIGATIONS: 1112 case MAPPINGS: 1113 for (Column col : columns) { 1114 hrow.getCells().add(gen.new Cell()); 1115 } 1116 break; 1117 case SUMMARY: 1118 hrow.getCells().add(gen.new Cell()); 1119 hrow.getCells().add(gen.new Cell()); 1120 hrow.getCells().add(gen.new Cell()); 1121 hrow.getCells().add(gen.new Cell(null, null, context.formatPhrase(RenderingContext.STRUC_DEF_CONT_TYPE), "", null)); 1122 } 1123 row.getSubRows().add(hrow); 1124 row = hrow; 1125 } 1126 1127 Row slicer = null; 1128 List<ElementChoiceGroup> groups = readChoices(element, children); 1129 boolean isExtension = Utilities.existsInList(tail(element.getPath()), "extension", "modifierExtension"); 1130 if (!element.prohibited()) { 1131 for (ElementDefinition child : children) { 1132 Row parent = null; 1133 if (child.hasSliceName()) { 1134 // ok, we're a slice 1135 if (slicer == null || !noTail(slicer.getId()).equals(child.getPath())) { 1136 parent = gen.new Row(); 1137 String anchorE = child.getPath(); 1138 anchorE = makeAnchorUnique(anchorE); 1139 parent.setId(context.prefixAnchor(anchorE)); 1140 parent.setAnchor(context.prefixAnchor(anchorE)); 1141 parent.setColor(context.getProfileUtilities().getRowColor(child, isConstraintMode)); 1142 parent.setLineColor(1); 1143 parent.setIcon("icon_slice.png", context.formatPhrase(RenderingContext.TEXT_ICON_SLICE)); 1144 parent.getCells().add(gen.new Cell(null, null, context.formatPhrase(RenderingContext.STRUC_DEF_SLICE_FOR, child.getName()), "", null)); 1145 switch (context.getStructureMode()) { 1146 case BINDINGS: 1147 case OBLIGATIONS: 1148 case MAPPINGS: 1149 for (Column col : columns) { 1150 parent.getCells().add(gen.new Cell()); 1151 } 1152 break; 1153 case SUMMARY: 1154 parent.getCells().add(gen.new Cell()); 1155 parent.getCells().add(gen.new Cell()); 1156 parent.getCells().add(gen.new Cell()); 1157 parent.getCells().add(gen.new Cell(null, null, context.formatPhrase(RenderingContext.STRUC_DEF_CONT_RULE), "", null)); 1158 break; 1159 } 1160 row.getSubRows().add(parent); 1161 slicer = parent; 1162 } else { 1163 parent = slicer; 1164 } 1165 } else { 1166 parent = chooseChildRowByGroup(gen, row, groups, child, element, isConstraintMode); 1167 } 1168 1169 if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) { 1170 slicer = genElement(status, defPath, gen, parent.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, slicer, mustSupport, rc, anchorPrefix, srcSD, columns, res, false, mappings, diff); 1171 } 1172 } 1173 } 1174 // if (!snapshot && (extensions == null || !extensions)) 1175 // for (ElementDefinition child : children) 1176 // if (child.getPath().endsWith(".extension") || child.getPath().endsWith(".modifierExtension")) 1177 // genElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, true, false, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants); 1178 } 1179 if (typesRow != null && !element.prohibited() && context.getStructureMode() == StructureDefinitionRendererMode.SUMMARY) { 1180 makeChoiceRows(typesRow.getSubRows(), element, gen, corePath, profileBaseFileName, mustSupport, srcSD); 1181 } 1182 } 1183 return slicingRow; 1184 } 1185 1186 private boolean elementOrDescendentsHaveObligations(List<ElementDefinition> all, ElementDefinition element) { 1187 if (element.hasObligations()) { 1188 return true; 1189 } 1190 int i = all.indexOf(element) + 1; 1191 while (i < all.size() && all.get(i).getPath().startsWith(element.getPath()+".")) { 1192 if (all.get(i).hasObligations()) { 1193 return true; 1194 } 1195 i++; 1196 } 1197 return false; 1198 } 1199 1200 private String noTail(String id) { 1201 if (id.contains(".")) { 1202 String t = id.substring(id.lastIndexOf(".")+1); 1203 if (Utilities.isInteger(t)) { 1204 return id.substring(0, id.lastIndexOf(".")); 1205 } 1206 } 1207 return id; 1208 } 1209 1210 private String makeAnchorUnique(String anchor) { 1211 if (anchors.containsKey(anchor)) { 1212 int c = anchors.get(anchor)+1; 1213 anchors.put(anchor, c); 1214 anchor = anchor+"."+c; 1215 } else { 1216 anchors.put(anchor, 1); 1217 } 1218 return anchor; 1219 } 1220 1221 private boolean isTypeSlice(ElementDefinition child) { 1222 ElementDefinition derived = (ElementDefinition) child.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER); 1223 return derived != null && derived.getBase().getPath().endsWith("[x]"); 1224 } 1225 1226 private boolean isExtension(ElementDefinition element) { 1227 if (element.getType().isEmpty()) { 1228 return true; 1229 } 1230 String type = element.getTypeFirstRep().getWorkingCode(); 1231 return "Extension".equals(type); 1232 } 1233 1234 private void genElementObligations(HierarchicalTableGenerator gen, ElementDefinition element, List<Column> columns, Row row, String corePath, StructureDefinition profile, boolean obLists) throws IOException { 1235 for (Column col : columns) { 1236 Cell gc = gen.new Cell(); 1237 row.getCells().add(gc); 1238 ObligationsRenderer obr = new ObligationsRenderer(corePath, profile, element.getPath(), context, null, this, obLists); 1239 obr.seeObligations(element, col.id); 1240 obr.renderList(gen, gc); 1241 } 1242 } 1243 1244 private void genElementMappings(HierarchicalTableGenerator gen, ElementDefinition element, List<Column> columns, Row row, String corePath, StructureDefinition profile, List<ModelMappingProvider> mappings) throws IOException { 1245 for (ModelMappingProvider mm : mappings) { 1246 Cell gc = gen.new Cell(); 1247 row.getCells().add(gc); 1248 Piece p = gc.addText(""); 1249 XhtmlNode div = new XhtmlNode(NodeType.Element, "div"); 1250 mm.render(element, div); 1251 p.addHtml(div); 1252 } 1253 } 1254 1255 private String displayForUsage(Coding c) { 1256 if (c.hasDisplay()) { 1257 return c.getDisplay(); 1258 } 1259 if ("http://terminology.hl7.org/CodeSystem/usage-context-type".equals(c.getSystem())) { 1260 return c.getCode(); 1261 } 1262 return c.getCode(); 1263 } 1264 1265 private void genElementBindings(HierarchicalTableGenerator gen, ElementDefinition element, List<Column> columns, Row row, StructureDefinition profile, String corepath) { 1266 for (Column col : columns) { 1267 Cell gc = gen.new Cell(); 1268 row.getCells().add(gc); 1269 List<ElementDefinitionBindingAdditionalComponent> bindings = collectBindings(element, col.id); 1270 if (bindings.size() > 0) { 1271 Piece p = gen.new Piece(null); 1272 gc.addPiece(p); 1273 new AdditionalBindingsRenderer(context.getPkp(), corepath, profile, element.getPath(), context, null, this).render(p.getChildren(), bindings, profile); 1274 } 1275 } 1276 } 1277 1278 private List<ElementDefinitionBindingAdditionalComponent> collectBindings(ElementDefinition element, String type) { 1279 List<ElementDefinitionBindingAdditionalComponent> res = new ArrayList<>(); 1280 if (element.hasBinding()) { 1281 ElementDefinitionBindingComponent b = element.getBinding(); 1282 if (b.hasStrength() && type.equals(b.getStrength().toCode())) { 1283 ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); 1284 res.add(ab.setAny(false).setDocumentation(b.getDescription()).setValueSet(b.getValueSet())); 1285 } 1286 if ("maximum".equals(type) && b.hasExtension(ExtensionDefinitions.EXT_MAX_VALUESET)) { 1287 ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); 1288 res.add(ab.setAny(false).setValueSet(ExtensionUtilities.readStringExtension(b, ExtensionDefinitions.EXT_MAX_VALUESET))); 1289 } 1290 if ("minimum".equals(type) && b.hasExtension(ExtensionDefinitions.EXT_MIN_VALUESET)) { 1291 ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); 1292 res.add(ab.setAny(false).setValueSet(ExtensionUtilities.readStringExtension(b, ExtensionDefinitions.EXT_MIN_VALUESET))); 1293 } 1294 for (ElementDefinitionBindingAdditionalComponent t : b.getAdditional()) { 1295 if (type.equals(t.getPurpose().toCode())) { 1296 res.add(t); 1297 } 1298 } 1299 for (Extension ext : b.getExtensionsByUrl(ExtensionDefinitions.EXT_BINDING_ADDITIONAL)) { 1300 if (type.equals(ext.getExtensionString("purpose"))) { 1301 ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); 1302 if (ext.hasExtension("any")) { 1303 ab.setAny(ExtensionUtilities.readBooleanExtension(ext, "any")); 1304 } 1305 if (ext.hasExtension("purpose")) { 1306 ab.setPurpose(AdditionalBindingPurposeVS.fromCode(ExtensionUtilities.readStringExtension(ext, "purpose"))); 1307 } 1308 if (ext.hasExtension("documentation")) { 1309 ab.setDocumentation(ExtensionUtilities.readStringExtension(ext, "documentation")); 1310 } 1311 if (ext.hasExtension("shortDoco")) { 1312 ab.setShortDoco(ExtensionUtilities.readStringExtension(ext, "shortDoco")); 1313 } 1314 if (ExtensionUtilities.hasExtension(ext, "usage")) { 1315 ab.addUsage(ext.getExtensionByUrl("usage").getValueUsageContext()); 1316 } 1317 if (ext.hasExtension("valueSet")) { 1318 ab.setValueSet(ExtensionUtilities.readStringExtension(ext, "valueSet")); 1319 } 1320 res.add(ab); 1321 } 1322 } 1323 } 1324 return res; 1325 } 1326 1327 public Cell genElementNameCell(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath, 1328 String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef, 1329 boolean ext, UnusedTracker used, String ref, String sName, List<ElementDefinition> elements) throws IOException { 1330 String hint = ""; 1331 hint = checkAdd(hint, element.hasSliceName() ? context.formatPhrase(RenderingContext.STRUC_DEF_SLICE_PAR, element.getSliceName()) : ""); 1332 if (hasDef && element.hasDefinition()) { 1333 hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : "")); 1334 hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement())); 1335 } 1336 if (element.hasSlicing() && slicesExist(elements, element)) { // some elements set up slicing but don't actually slice, so we don't augment the name 1337 sName = context.formatPhrase(RenderingContext.STRUC_DEF_SLICE_FOR, sName); 1338 } 1339 Cell left = gen.new Cell(null, ref, sName, hint, null); 1340 row.getCells().add(left); 1341 if (profile.hasExtension(ExtensionDefinitions.EXT_TYPE_PARAMETER)) { 1342 Extension etp = profile.getExtensionByUrl(ExtensionDefinitions.EXT_TYPE_PARAMETER); 1343 String name = etp.getExtensionString("name"); 1344 String type = etp.getExtensionString("type"); 1345 StructureDefinition t = context.getContext().fetchTypeDefinition(type); 1346 if (t == null) { 1347 left.addPiece(gen.new Piece(null, "<"+name+" : "+type+">", null)); 1348 } else if (t.getWebPath() == null) { 1349 left.addPiece(gen.new Piece(type, "<"+name+" : "+t.present()+">", null)); 1350 } else { 1351 left.addPiece(gen.new Piece(t.getWebPath(), "<"+name+" : "+t.present()+">", null)); 1352 } 1353 } 1354 return left; 1355 } 1356 1357 public List<Cell> genElementCells(RenderingStatus status, HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath, 1358 String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef, 1359 boolean ext, UnusedTracker used, String ref, Cell nameCell, boolean mustSupport, boolean allowSubRows, RenderingContext rc, boolean walksIntoThis, String defPath, String anchorPrefix, List<ElementDefinition> inScopeElements, ResourceWrapper resource, boolean diff) throws IOException { 1360 List<Cell> res = new ArrayList<>(); 1361 Cell gc = gen.new Cell(); 1362 row.getCells().add(gc); 1363 res.add(gc); 1364 if (element != null && element.getIsModifier()) { 1365 checkForNoChange(element.getIsModifierElement(), gc.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_MOD)), "?!", null, null, null, false)); 1366 } 1367 if (element != null) { 1368 if (element.getMustSupport() && element.hasExtension(ExtensionDefinitions.EXT_OBLIGATION_CORE, ExtensionDefinitions.EXT_OBLIGATION_TOOLS)) { 1369 checkForNoChange(element.getMustSupportElement(), gc.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_OBLIG_SUPP)), "SO", "white", "red", null, false)); 1370 } else if (element.getMustSupport()) { 1371 checkForNoChange(element.getMustSupportElement(), gc.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_ELE_MUST_SUPP)), "S", "white", "red", null, false)); 1372 } else if (element != null && element.hasExtension(ExtensionDefinitions.EXT_OBLIGATION_CORE, ExtensionDefinitions.EXT_OBLIGATION_TOOLS)) { 1373 checkForNoChange(element.getMustSupportElement(), gc.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_OBLIG)), "O", "white", "red", null, false)); 1374 } 1375 } 1376 if (element != null && element.getIsSummary()) { 1377 checkForNoChange(element.getIsSummaryElement(), gc.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_ELE_INCLUDED)), "\u03A3", null, null, null, false)); 1378 } 1379 if (element != null && element.getMustHaveValue()) { 1380 checkForNoChange(element.getMustHaveValueElement(), gc.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_ELE)), "V", "maroon", null, null, true)); 1381 } 1382 if (element != null && (hasNonBaseConstraints(element.getConstraint()) || hasNonBaseConditions(element.getCondition()))) { 1383 Piece p = gc.addText(CONSTRAINT_CHAR); 1384 p.setHint((context.formatPhrase(RenderingContext.STRUC_DEF_ELE_AFFECTED, listConstraintsAndConditions(element), ")"))); 1385 p.addStyle(CONSTRAINT_STYLE); 1386 p.setReference(Utilities.pathURL(VersionUtilities.getSpecUrl(context.getWorker().getVersion()), "conformance-rules.html#constraints")); 1387 } 1388 if (element != null && element.hasExtension(ExtensionDefinitions.EXT_STANDARDS_STATUS)) { 1389 StandardsStatus ss = StandardsStatus.fromCode(element.getExtensionString(ExtensionDefinitions.EXT_STANDARDS_STATUS)); 1390 gc.addStyledText(context.formatPhrase(RenderingContext.STRUC_DEF_STAND_STATUS) +ss.toDisplay(), ss.getAbbrev(), context.formatPhrase(RenderingContext.STRUC_DEF_BLACK), ss.getColor(), context.getWorker().getSpecUrl()+"versions.html#std-process", true); 1391 } 1392 1393 ExtensionContext extDefn = null; 1394 if (ext) { 1395 if (element != null) { 1396 if (element.getType().size() == 1 && element.getType().get(0).hasProfile()) { 1397 String eurl = element.getType().get(0).getProfile().get(0).getValue(); 1398 extDefn = locateExtension(StructureDefinition.class, eurl, profile); 1399 if (extDefn == null) { 1400 res.add(genCardinality(gen, element, row, hasDef, used, null)); 1401 res.add(addCell(row, gen.new Cell(null, null, "?gen-e1? "+element.getType().get(0).getProfile(), null, null))); 1402 res.add(generateDescription(status, gen, row, element, (ElementDefinition) element.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER), profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc, inScopeElements, resource)); 1403 } else { 1404 String name = element.hasSliceName() ? element.getSliceName() : urlFragmentOrTail(eurl); 1405// disable 26-02-2025 GDG - this just makes things inconsistent, and why do this? nameCell.getPieces().get(0).setText(name); 1406 // left.getPieces().get(0).setReference((String) extDefn.getExtensionStructure().getTag("filename")); 1407 nameCell.getPieces().get(0).setHint((context.formatPhrase(RenderingContext.STRUC_DEF_EX_URL, extDefn.getUrl()))); 1408 res.add(genCardinality(gen, element, row, hasDef, used, extDefn.getElement())); 1409 ElementDefinition valueDefn = walksIntoThis ? null : extDefn.getExtensionValueDefinition(); 1410 if (valueDefn != null && !"0".equals(valueDefn.getMax())) 1411 res.add(genTypes(gen, row, valueDefn, profileBaseFileName, profile, corePath, imagePath, root, mustSupport, diff)); 1412 else // if it's complex, we just call it nothing 1413 // genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile); 1414 res.add(addCell(row, gen.new Cell(null, null, "("+(context.formatPhrase(RenderingContext.STRUC_DEF_COMPLEX))+")", null, null))); 1415 res.add(generateDescription(status, gen, row, element, extDefn.getElement(), null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport, allowSubRows, rc, inScopeElements, resource)); 1416 } 1417 } else { 1418 res.add(genCardinality(gen, element, row, hasDef, used, null)); 1419 if ("0".equals(element.getMax())) 1420 res.add(addCell(row, gen.new Cell())); 1421 else 1422 res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport, diff)); 1423 res.add(generateDescription(status, gen, row, element, (ElementDefinition) element.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER), null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc, inScopeElements, resource)); 1424 } 1425 } 1426 } else if (element != null) { 1427 res.add(genCardinality(gen, element, row, hasDef, used, null)); 1428 if (hasDef && !"0".equals(element.getMax()) && typesRow == null) 1429 res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport, diff)); 1430 else 1431 res.add(addCell(row, gen.new Cell())); 1432 res.add(generateDescription(status, gen, row, element, (ElementDefinition) element.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER), null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc, inScopeElements, resource)); 1433 } 1434 return res; 1435 } 1436 1437 private Cell genCardinality(HierarchicalTableGenerator gen, ElementDefinition definition, Row row, boolean hasDef, UnusedTracker tracker, ElementDefinition fallback) { 1438 IntegerType min = !hasDef ? new IntegerType() : definition.hasMinElement() ? definition.getMinElement() : new IntegerType(); 1439 StringType max = !hasDef ? new StringType() : definition.hasMaxElement() ? definition.getMaxElement() : new StringType(); 1440 if (min.isEmpty() && definition.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER) != null) { 1441 ElementDefinition base = (ElementDefinition) definition.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER); 1442 if (base.hasMinElement()) { 1443 min = base.getMinElement().copy(); 1444 min.setUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS, true); 1445 } 1446 } 1447 if (max.isEmpty() && definition.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER) != null) { 1448 ElementDefinition base = (ElementDefinition) definition.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER); 1449 if (base.hasMaxElement()) { 1450 max = base.getMaxElement().copy(); 1451 max.setUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS, true); 1452 } 1453 } 1454 if (min.isEmpty() && fallback != null) 1455 min = fallback.getMinElement(); 1456 if (max.isEmpty() && fallback != null) 1457 max = fallback.getMaxElement(); 1458 1459 if (!max.isEmpty()) 1460 tracker.used = !max.getValue().equals("0"); 1461 1462 String hint = null; 1463 if (max.hasValue() && min.hasValue() && "*".equals(max.getValue()) && 0 == min.getValue()) { 1464 if (definition.hasExtension(ExtensionDefinitions.EXT_JSON_EMPTY)) { 1465 String code = ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_JSON_EMPTY); 1466 if ("present".equals(code)) { 1467 hint = context.formatPhrase(RenderingContext.STRUC_DEF_JSON_IS); 1468 } else { 1469 hint = context.formatPhrase(RenderingContext.STRUC_DEF_JSON_MAY); 1470 } 1471 } 1472 } 1473 Cell cell = gen.new Cell(null, null, null, null, null); 1474 row.getCells().add(cell); 1475 if (!min.isEmpty() || !max.isEmpty()) { 1476 cell.addPiece(checkForNoChange(min, gen.new Piece(null, !min.hasValue() ? "" : Integer.toString(min.getValue()), hint))); 1477 cell.addPiece(checkForNoChange(min, max, gen.new Piece(null, "..", hint))); 1478 cell.addPiece(checkForNoChange(max, gen.new Piece(null, !max.hasValue() ? "" : max.getValue(), hint))); 1479 } 1480 return cell; 1481 } 1482 1483 1484 private boolean usesMustSupport(List<ElementDefinition> list) { 1485 for (ElementDefinition ed : list) 1486 if (ed.hasMustSupport() && ed.getMustSupport()) 1487 return true; 1488 return false; 1489 } 1490 1491 1492 1493 private Row chooseChildRowByGroup(HierarchicalTableGenerator gen, Row row, List<ElementChoiceGroup> groups, ElementDefinition element, ElementDefinition parent, boolean isConstraintMode) { 1494 String name = tail(element.getPath()); 1495 for (ElementChoiceGroup grp : groups) { 1496 if (grp.getElements().contains(name)) { 1497 if (grp.getRow() == null) { 1498 grp.setRow(makeChoiceElementRow(gen, row, grp, parent, isConstraintMode)); 1499 } 1500 return grp.getRow(); 1501 } 1502 } 1503 return row; 1504 } 1505 1506 private Row makeChoiceElementRow(HierarchicalTableGenerator gen, Row prow, ElementChoiceGroup grp, ElementDefinition parent, boolean isConstraintMode) { 1507 if (context.getStructureMode() != StructureDefinitionRendererMode.SUMMARY) { 1508 return prow; 1509 } 1510 Row row = gen.new Row(); 1511 row.setId(context.prefixAnchor(parent.getPath()+"-"+grp.getName())); 1512 row.setAnchor(context.prefixAnchor(parent.getPath()+"-"+grp.getName())); 1513 row.setColor(context.getProfileUtilities().getRowColor(parent, isConstraintMode)); 1514 row.setLineColor(1); 1515 row.setIcon("icon_choice.gif", context.formatPhrase(RenderingContext.TEXT_ICON_CHOICE)); 1516 row.getCells().add(gen.new Cell(null, null, context.formatPhrase(RenderingContext.STRUC_DEF_CHOICE), "", null)); 1517 row.getCells().add(gen.new Cell()); 1518 row.getCells().add(gen.new Cell(null, null, (grp.isMandatory() ? "1" : "0")+"..1", "", null)); 1519 row.getCells().add(gen.new Cell()); 1520 row.getCells().add(gen.new Cell()); 1521 prow.getSubRows().add(row); 1522 return row; 1523 } 1524 1525 1526 private String urlFragmentOrTail(String path) { 1527 if (path.contains("#")) 1528 return path.substring(path.lastIndexOf('#')+1); 1529 if (path.contains("/")) 1530 return path.substring(path.lastIndexOf('/')+1); 1531 else 1532 return path; 1533 1534 } 1535 1536 private boolean standardExtensionSlicing(ElementDefinition element) { 1537 String t = tail(element.getPath()); 1538 return (t.equals("extension") || t.equals("modifierExtension")) 1539 && element.getSlicing().getRules() != SlicingRules.CLOSED && element.getSlicing().getDiscriminator().size() == 1 && element.getSlicing().getDiscriminator().get(0).getPath().equals("url") && element.getSlicing().getDiscriminator().get(0).getType().equals(DiscriminatorType.VALUE); 1540 } 1541 1542 public Cell generateDescription(RenderingStatus status, HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc, ResourceWrapper res) throws IOException, FHIRException { 1543 return generateDescription(status, gen, row, definition, fallback, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc, new ArrayList<ElementDefinition>(), res); 1544 } 1545 1546 public Cell generateDescription(RenderingStatus status, HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc, List<ElementDefinition> inScopeElements, ResourceWrapper res) throws IOException, FHIRException { 1547 return generateDescription(status, gen, row, definition, fallback, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc, inScopeElements, res); 1548 } 1549 1550 public Cell generateDescription(RenderingStatus status, HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc, List<ElementDefinition> inScopeElements, ResourceWrapper res) throws IOException, FHIRException { 1551 Cell c = gen.new Cell(); 1552 row.getCells().add(c); 1553 1554 if (logicalModel && ExtensionUtilities.hasAnyOfExtensions(profile, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED)) { 1555 if (root) { 1556 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_XML_NAME)) + ": ", null).addStyle("font-weight:bold")); 1557 c.getPieces().add(gen.new Piece(null, ExtensionUtilities.readStringExtension(profile, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED), null)); 1558 } else if (!root && ExtensionUtilities.hasAnyOfExtensions(definition, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED) && 1559 !ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED).equals(ExtensionUtilities.readStringExtension(profile, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED))) { 1560 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_XML_NAME)) + ": ", null).addStyle("font-weight:bold")); 1561 c.getPieces().add(gen.new Piece(null, ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED), null)); 1562 } 1563 } 1564 if (root) { 1565 if (profile != null && profile.getAbstract()) { 1566 if (!c.getPieces().isEmpty()) { 1567 c.addPiece(gen.new Piece("br")); 1568 } 1569 c.addPiece(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_ABSTRACT) + " " + (profile.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profile" : "type") + ". ", null)); 1570 1571 List<StructureDefinition> children = new ArrayList<>(); 1572 for (StructureDefinition sd : context.getWorker().fetchResourcesByType(StructureDefinition.class)) { 1573 if (sd.hasBaseDefinition() && sd.getBaseDefinitionNoVersion().equals(profile.getUrl())) { 1574 children.add(sd); 1575 } 1576 } 1577 if (!children.isEmpty()) { 1578 c.addPiece(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_CHILD) + " " + (profile.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profiles" : "types") + ": ", null)); 1579 boolean first = true; 1580 for (StructureDefinition sd : children) { 1581 if (first) first = false; 1582 else c.addPiece(gen.new Piece(null, ", ", null)); 1583 c.addPiece(gen.new Piece(sd.getWebPath(), sd.getName(), null)); 1584 } 1585 } 1586 } 1587 } 1588 if (definition.getPath().endsWith("url") && definition.hasFixed()) { 1589 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\"" + buildJson(definition.getFixed()) + "\"", null).addStyle("color: darkgreen"))); 1590 } else { 1591 if (definition != null && definition.hasShort()) { 1592 if (!c.getPieces().isEmpty()) { 1593 c.addPiece(gen.new Piece("br")); 1594 } 1595 Piece piece = checkForNoChange(definition.getShortElement(), gen.new Piece(null, gt(definition.getShortElement()), null)); 1596 if (!definition.getShortElement().hasUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS)) { 1597 piece.setUnderived(true); 1598 } 1599 c.addPiece(piece); 1600 } else if (fallback != null && fallback.hasShort()) { 1601 if (!c.getPieces().isEmpty()) { 1602 c.addPiece(gen.new Piece("br")); 1603 } 1604 Piece piece = gen.new Piece(null, gt(fallback.getShortElement()), null).addStyle("opacity: 0.5"); 1605 if (!definition.getShortElement().hasUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS)) { 1606 piece.setUnderived(true); 1607 } 1608 c.addPiece(piece); 1609 } 1610 if (url != null) { 1611 if (!c.getPieces().isEmpty()) 1612 c.addPiece(gen.new Piece("br")); 1613 String fullUrl = url.startsWith("#") ? baseURL + url : url; 1614 StructureDefinition ed = context.getProfileUtilities().findProfile(url, profile); 1615 String ref = null; 1616 String ref2 = null; 1617 String fixedUrl = null; 1618 if (ed != null) { 1619 ref = ed.getWebPath(); 1620 fixedUrl = getFixedUrl(ed); 1621 if (fixedUrl != null) {// if its null, we guess that it's not a profiled extension? 1622 if (fixedUrl.equals(url)) 1623 fixedUrl = null; 1624 else { 1625 StructureDefinition ed2 = context.getProfileUtilities().findProfile(fixedUrl, profile); 1626 if (ed2 != null) { 1627 String p2 = ed2.getWebPath(); 1628 if (p2 != null) { 1629 ref2 = p2.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p2 : Utilities.pathURL(corePath, p2); 1630 } 1631 } 1632 } 1633 } 1634 } 1635 if (fixedUrl == null) { 1636 if (!Utilities.noString(fullUrl)) { 1637 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_URL)) + ": ", null).addStyle("font-weight:bold")); 1638 c.getPieces().add(gen.new Piece(ref, fullUrl, null)); 1639 } 1640 } else { 1641 // reference to a profile take on the extension show the base URL 1642 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_URL)) + ": ", null).addStyle("font-weight:bold")); 1643 c.getPieces().add(gen.new Piece(ref2, fixedUrl, null)); 1644 c.getPieces().add(gen.new Piece(null, (" " + context.formatPhrase(RenderingContext.STRUC_DEF_PROFILED) + " ") + " ", null).addStyle("font-weight:bold")); 1645 c.getPieces().add(gen.new Piece(ref, fullUrl, null)); 1646 1647 } 1648 } 1649 1650 if (root) { 1651 if (profile.getDerivation() == TypeDerivationRule.SPECIALIZATION) { 1652 List<SourcedElementDefinition> ancestors = new ArrayList<>(); 1653 getAncestorElements(new ArrayList<>(), profile, ancestors); 1654 if (ancestors.size() > 0) { 1655 c.addPiece(gen.new Piece("br")); 1656 c.addPiece(gen.new Piece("br")); 1657 c.addPiece(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_ELEMENTS), null)); 1658 boolean first = true; 1659 for (SourcedElementDefinition ed : ancestors) { 1660 if (first) 1661 first = false; 1662 else 1663 c.addPiece(gen.new Piece(null, ", ", null)); 1664 c.addPiece(gen.new Piece(ed.getProfile().getWebPath(), (isAttr(ed) ? "@" : "") + ed.getDefinition().getName(), ed.getDefinition().getDefinition())); 1665 } 1666 } 1667 } 1668 if (profile.hasExtension(ExtensionDefinitions.EXT_RESOURCE_IMPLEMENTS)) { 1669 c.getPieces().add(gen.new Piece("br")); 1670 c.getPieces().add(gen.new Piece(spec("uml.html#interfaces"), context.formatPhrase(RenderingContext.STRUC_DEF_IMPLEMENTS), null)); 1671 boolean first = true; 1672 for (Extension fi : profile.getExtensionsByUrl(ExtensionDefinitions.EXT_RESOURCE_IMPLEMENTS)) { 1673 if (first) { 1674 c.getPieces().add(gen.new Piece(null, ": ", null)); 1675 first = false; 1676 } else { 1677 c.getPieces().add(gen.new Piece(null, ", ", null)); 1678 } 1679 StructureDefinition sdt = context.getWorker().fetchResource(StructureDefinition.class, fi.getValue().primitiveValue()); 1680 if (sdt != null) { 1681 c.getPieces().add(gen.new Piece(sdt.getWebPath(), sdt.present(), sdt.getDescription())); 1682 } else { 1683 c.getPieces().add(gen.new Piece(null, fi.getValue().primitiveValue(), "Unknown: "+fi.getValue().primitiveValue())); 1684 } 1685 } 1686 } 1687 } 1688 1689 if (definition.hasSlicing()) { 1690 if (!c.getPieces().isEmpty()) { 1691 c.addPiece(gen.new Piece("br")); 1692 } 1693 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_SLICE)) + ": ", null).addStyle("font-weight:bold")); 1694 c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); 1695 } 1696 if (!definition.getPath().contains(".") && ExtensionUtilities.hasExtension(profile, ExtensionDefinitions.EXT_BINDING_STYLE)) { 1697 if (!c.getPieces().isEmpty()) { 1698 c.addPiece(gen.new Piece("br")); 1699 } 1700 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_BINDING)) + ": ", null).addStyle("font-weight:bold")); 1701 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_SET) + " "), null)); 1702 c.getPieces().add(gen.new Piece(null, ExtensionUtilities.readStringExtension(profile, ExtensionDefinitions.EXT_BINDING_STYLE), null)); 1703 c.getPieces().add(gen.new Piece(null, " " + context.formatPhrase(RenderingContext.STRUC_DEF_BINDING_STYLE), null)); 1704 } 1705 if (definition.hasValueAlternatives()) { 1706 addCanonicalList(gen, c, definition.getValueAlternatives(), "The primitive value may be replaced by the extension", true, profile); 1707 } 1708 if (definition.hasExtension(ExtensionDefinitions.EXT_IMPLIED_PREFIX)) { 1709 if (!c.getPieces().isEmpty()) { 1710 c.addPiece(gen.new Piece("br")); 1711 } 1712 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_ELE_READ) + " "), null)); 1713 Piece piece = gen.new Piece("code"); 1714 piece.addHtml(new XhtmlNode(NodeType.Text).setContent(ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_IMPLIED_PREFIX))); 1715 c.getPieces().add(piece); 1716 c.getPieces().add(gen.new Piece(null, " " + (context.formatPhrase(RenderingContext.STRUC_DEF_PREFIXED)), null)); 1717 } 1718 if (root && ProfileUtilities.isModifierExtension(profile)) { 1719 if (!c.getPieces().isEmpty()) { 1720 c.addPiece(gen.new Piece("br")); 1721 } 1722 c.addPiece(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_MODIFIER_EXT), null).addStyle("font-weight:bold")); 1723 1724 } 1725 1726 if (definition.hasExtension(ExtensionDefinitions.EXT_EXTENSION_STYLE_NEW, ExtensionDefinitions.EXT_EXTENSION_STYLE_DEPRECATED)) { 1727 if (!c.getPieces().isEmpty()) { 1728 c.addPiece(gen.new Piece("br")); 1729 } 1730 String es = definition.getExtensionString(ExtensionDefinitions.EXT_EXTENSION_STYLE_NEW, ExtensionDefinitions.EXT_EXTENSION_STYLE_DEPRECATED); 1731 if ("named-elements".equals(es)) { 1732 if (rc.hasLink(KnownLinkType.JSON_NAMES)) { 1733 c.getPieces().add(gen.new Piece(rc.getLink(KnownLinkType.JSON_NAMES, true), context.formatPhrase(RenderingContext.STRUC_DEF_EXT_JSON), null)); 1734 } else { 1735 c.getPieces().add(gen.new Piece(ExtensionDefinitions.WEB_EXTENSION_STYLE, context.formatPhrase(RenderingContext.STRUC_DEF_EXT_JSON), null)); 1736 } 1737 } 1738 } 1739 if (definition.typeSummary().equals("Narrative")) { 1740 if (!c.getPieces().isEmpty()) { 1741 c.addPiece(gen.new Piece("br")); 1742 } 1743 Set<String> statusCodes = determineNarrativeStatus(definition, profile, snapshot); 1744 List<String> langCtrl = ExtensionUtilities.readStringExtensions(definition, ExtensionDefinitions.EXT_NARRATIVE_LANGUAGE_CONTROL); 1745 String level = ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_NARRATIVE_SOURCE_CONTROL); 1746 // what do we want to do here? 1747 // the narrative might be constrained in these ways: 1748 // extension http://hl7.org/fhir/StructureDefinition/narrative-language-control 1749 // extension http://hl7.org/fhir/StructureDefinition/narrative-source-control 1750 // restrictions on status 1751 // direct linkage to data requirements 1752 if ((statusCodes.isEmpty() || statusCodes.size() == 4) && level == null && langCtrl.isEmpty()) { 1753 c.getPieces().add(gen.new Piece(null, "This profile does not constrain the narrative in regard to content, language, or traceability to data elements", null).addStyle("font-weight:bold")); 1754 } else { 1755 if ((statusCodes.isEmpty() || statusCodes.size() == 4)) { 1756 c.getPieces().add(gen.new Piece(null, "This profile does not constrain the narrative content by fixing the status codes", null).addStyle("font-weight:bold")); 1757 } else { 1758 c.getPieces().add(gen.new Piece(null, "This profile constrains the narrative content by fixing the status codes to " + CommaSeparatedStringBuilder.join2(", ", " and ", statusCodes), null).addStyle("font-weight:bold")); 1759 } 1760 c.addPiece(gen.new Piece("br")); 1761 String ltx = null; 1762 if (langCtrl.isEmpty()) { 1763 ltx = "This profile does not constrain the narrative in regard to language specific sections"; 1764 } else if (langCtrl.size() == 1 && langCtrl.get(0).equals("_no")) { 1765 ltx = "This profile constrains the narrative to not contain any language specific sections"; 1766 } else if (langCtrl.size() == 1 && langCtrl.get(0).equals("_yes")) { 1767 ltx = "This profile constrains the narrative to contain language sections, but doesn't make rules about them"; 1768 } else { 1769 int i = langCtrl.indexOf("_resource"); 1770 if (i == -1) { 1771 ltx = "This profile constrains the narrative to contain language sections for the languages " + CommaSeparatedStringBuilder.join2(", ", " and ", langCtrl); 1772 } else { 1773 langCtrl.remove(i); 1774 if (langCtrl.size() == 0) { 1775 ltx = "This profile constrains the narrative to contain a language sections in the same language as the resource"; 1776 } else { 1777 ltx = "This profile constrains the narrative to contain a language sections in the same language as the resource, and also for the languages " + CommaSeparatedStringBuilder.join2(", ", " and ", langCtrl); 1778 } 1779 } 1780 } 1781 c.getPieces().add(gen.new Piece(null, ltx, null).addStyle("font-weight:bold")); 1782 c.addPiece(gen.new Piece("br")); 1783 1784 if (level == null) { 1785 c.getPieces().add(gen.new Piece(null, "This profile does not constrain the narrative in regard to traceability to data elements", null).addStyle("font-weight:bold")); 1786 } else { 1787 c.getPieces().add(gen.new Piece(null, "This profile indicates that if there are elements in the narrative without a source-type class, and " + level + " will be emitted", null).addStyle("font-weight:bold")); 1788 } 1789 } 1790 } 1791 if (definition.hasExtension(ExtensionDefinitions.EXT_DATE_FORMAT)) { 1792 String df = ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_DATE_FORMAT); 1793 if (df != null) { 1794 if (!c.getPieces().isEmpty()) { 1795 c.addPiece(gen.new Piece("br")); 1796 } 1797 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_DATE, df) + " "), null)); 1798 } 1799 } 1800 if (definition.hasExtension(ExtensionDefinitions.EXT_ID_EXPECTATION)) { 1801 String ide = ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_ID_EXPECTATION); 1802 if (ide.equals("optional")) { 1803 if (!c.getPieces().isEmpty()) { 1804 c.addPiece(gen.new Piece("br")); 1805 } 1806 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_ID_IS), null)); 1807 } else if (ide.equals("required")) { 1808 if (!c.getPieces().isEmpty()) { 1809 c.addPiece(gen.new Piece("br")); 1810 } 1811 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_ID_MAY), null)); 1812 } else if (ide.equals("required")) { 1813 if (!c.getPieces().isEmpty()) { 1814 c.addPiece(gen.new Piece("br")); 1815 } 1816 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_ID_NOT_ALLOW), null)); 1817 } 1818 } 1819 if (definition.hasExtension(ExtensionDefinitions.EXT_ID_CHOICE_GROUP)) { 1820 if (!c.getPieces().isEmpty()) { 1821 c.addPiece(gen.new Piece("br")); 1822 } 1823 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_CHOICE_GRP)) + ": ", null).addStyle("font-weight:bold")); 1824 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_REPEAT), null)); 1825 } 1826 if (definition.hasExtension(ExtensionDefinitions.EXT_XML_NAME, ExtensionDefinitions.EXT_XML_NAME_DEPRECATED)) { 1827 if (!c.getPieces().isEmpty()) { 1828 c.addPiece(gen.new Piece("br")); 1829 } 1830 if (definition.hasExtension(ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED)) { 1831 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_XML)) + ": ", null).addStyle("font-weight:bold")); 1832 c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ExtensionDefinitions.EXT_XML_NAME, ExtensionDefinitions.EXT_XML_NAME_DEPRECATED), null)); 1833 c.getPieces().add(gen.new Piece(null, " (", null)); 1834 c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED), null)); 1835 c.getPieces().add(gen.new Piece(null, ")", null)); 1836 } else { 1837 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_XML_ELE)) + ": ", null).addStyle("font-weight:bold")); 1838 c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ExtensionDefinitions.EXT_XML_NAME, ExtensionDefinitions.EXT_XML_NAME_DEPRECATED), null)); 1839 } 1840 } else if (definition.hasExtension(ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED)) { 1841 if (!c.getPieces().isEmpty()) { 1842 c.addPiece(gen.new Piece("br")); 1843 } 1844 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_XML_NAME)) + ": ", null).addStyle("font-weight:bold")); 1845 c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED), null)); 1846 } 1847 if (definition.hasExtension(ExtensionDefinitions.EXT_JSON_EMPTY)) { 1848 if (!c.getPieces().isEmpty()) { 1849 c.addPiece(gen.new Piece("br")); 1850 } 1851 String code = ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_JSON_EMPTY); 1852 if ("present".equals(code)) { 1853 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_JSON_INFERRED), null)); 1854 } else { 1855 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_JSON_ARRAY), null)); 1856 } 1857 } 1858 String jn = ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_JSON_NAME, ExtensionDefinitions.EXT_JSON_NAME_DEPRECATED); 1859 if (!Utilities.noString(jn)) { 1860 if (!c.getPieces().isEmpty()) { 1861 c.addPiece(gen.new Piece("br")); 1862 } 1863 if (definition.getPath().contains(".")) { 1864 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_JSON_NAME)) + ": ", null).addStyle("font-weight:bold")); 1865 c.getPieces().add(gen.new Piece(null, jn, null)); 1866 } else { 1867 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_JSON_TYPE)) + ": ", null).addStyle("font-weight:bold")); 1868 Piece piece = gen.new Piece("code"); 1869 piece.addHtml(new XhtmlNode(NodeType.Text).setContent(jn)); 1870 c.getPieces().add(piece); 1871 } 1872 } 1873 1874 if (ExtensionUtilities.readBoolExtension(definition, ExtensionDefinitions.EXT_JSON_PRIMITIVE_CHOICE)) { 1875 if (!c.getPieces().isEmpty()) { 1876 c.addPiece(gen.new Piece("br")); 1877 } 1878 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_JSON_INFERRED), null)); 1879 } 1880 if (ExtensionUtilities.readBoolExtension(definition, ExtensionDefinitions.EXT_JSON_NULLABLE)) { 1881 if (!c.getPieces().isEmpty()) { 1882 c.addPiece(gen.new Piece("br")); 1883 } 1884 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_JSON_NULL), null)); 1885 } 1886 if (definition.hasExtension(ExtensionDefinitions.EXT_JSON_PROP_KEY)) { 1887 if (!c.getPieces().isEmpty()) { 1888 c.addPiece(gen.new Piece("br")); 1889 } 1890 String code = ExtensionUtilities.readStringExtension(definition, ExtensionDefinitions.EXT_JSON_PROP_KEY); 1891 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_SINGLE_JSON_OBJECTS, code), null)); 1892 } 1893 if (definition.hasExtension(ExtensionDefinitions.EXT_TYPE_SPEC)) { 1894 for (Extension e : definition.getExtensionsByUrl(ExtensionDefinitions.EXT_TYPE_SPEC)) { 1895 if (!c.getPieces().isEmpty()) { 1896 c.addPiece(gen.new Piece("br")); 1897 } 1898 String cond = ExtensionUtilities.readStringExtension(e, "condition"); 1899 String type = ExtensionUtilities.readStringExtension(e, "type"); 1900 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_JSON_IF), null)); 1901 Piece piece = gen.new Piece("code"); 1902 piece.addHtml(new XhtmlNode(NodeType.Text).setContent(cond)); 1903 c.getPieces().add(piece); 1904 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_THEN_TYPE) + " ", null)); 1905 StructureDefinition sd = context.getWorker().fetchTypeDefinition(type); 1906 if (sd == null) { 1907 piece = gen.new Piece("code"); 1908 piece.addHtml(new XhtmlNode(NodeType.Text).setContent(type)); 1909 c.getPieces().add(piece); 1910 } else { 1911 c.getPieces().add(gen.new Piece(sd.getWebPath(), sd.getTypeName(), null)); 1912 } 1913 } 1914 } 1915 if (root) { 1916 if (ExtensionUtilities.readBoolExtension(profile, ExtensionDefinitions.EXT_OBLIGATION_PROFILE_FLAG_NEW, ExtensionDefinitions.EXT_OBLIGATION_PROFILE_FLAG_OLD)) { 1917 if (!c.getPieces().isEmpty()) { 1918 c.addPiece(gen.new Piece("br")); 1919 } 1920 c.addPiece(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_OBLIG_ADD), null).addStyle("font-weight:bold")); 1921 } 1922 addCanonicalListExt(gen, c, profile.getExtensionsByUrl(ExtensionDefinitions.EXT_OBLIGATION_INHERITS_NEW, ExtensionDefinitions.EXT_OBLIGATION_INHERITS_OLD), "This profile picks up obligations and additional bindings from the profile", true, profile); 1923 addCanonicalListExt(gen, c, profile.getExtensionsByUrl(ExtensionDefinitions.EXT_SD_IMPOSE_PROFILE), "This profile also imposes the profile", true, profile); 1924 addCanonicalListExt(gen, c, profile.getExtensionsByUrl(ExtensionDefinitions.EXT_SD_COMPLIES_WITH_PROFILE), "This profile also complies with the profile", true, profile); 1925 1926 if (profile.getKind() == StructureDefinitionKind.LOGICAL) { 1927 Extension lt = ExtensionUtilities.getExtension(profile, ExtensionDefinitions.EXT_LOGICAL_TARGET); 1928 List<Extension> tc = ExtensionUtilities.getExtensions(profile, ExtensionDefinitions.EXT_TYPE_CHARACTERISTICS); 1929 Boolean canBeTarget = checkCanBeTarget(lt, tc); 1930 if (canBeTarget == null) { 1931 // don't say anything 1932 } else if (canBeTarget) { 1933 if (!c.getPieces().isEmpty()) { 1934 c.addPiece(gen.new Piece("br")); 1935 } 1936 c.addPiece(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_CAN_TARGET), null).addStyle("font-weight:bold")); 1937 } else { 1938 if (!c.getPieces().isEmpty()) { 1939 c.addPiece(gen.new Piece("br")); 1940 } 1941 c.addPiece(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_NOT_MARK), null).addStyle("font-weight:bold")); 1942 } 1943 1944 String ps = ExtensionUtilities.readStringExtension(profile, ExtensionDefinitions.EXT_PROFILE_STYLE); 1945 if (ps != null) { 1946 if (!c.getPieces().isEmpty()) { 1947 c.addPiece(gen.new Piece("br")); 1948 } 1949 if ("cda".equals(ps)) { 1950 c.addPiece(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_TEMPLATEID), null).addStyle("font-weight:bold")); 1951 } else { 1952 c.addPiece(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_UNKNOWN_APPROACH, ps) + " ", null).addStyle("font-weight:bold")); 1953 } 1954 } 1955 Extension lc = ExtensionUtilities.getExtension(profile, ExtensionDefinitions.EXT_LOGICAL_CONTAINER); 1956 if (lc != null && lc.hasValueUriType()) { 1957 if (!c.getPieces().isEmpty()) { 1958 c.addPiece(gen.new Piece("br")); 1959 } 1960 c.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_LOGICAL_CONT)) + ": ", context.formatPhrase(RenderingContext.STRUC_DEF_ROOT)).addStyle("font-weight:bold")); 1961 1962 String uri = lc.getValue().primitiveValue(); 1963 StructureDefinition lct = context.getContext().fetchTypeDefinition(uri); 1964 if (lct != null) { 1965 c.addPiece(gen.new Piece(lct.getWebPath(), lct.present(), null)); 1966 } else { 1967 c.addPiece(gen.new Piece(null, uri, null)); 1968 } 1969 } 1970 } 1971 } 1972 if (definition != null) { 1973 ElementDefinitionBindingComponent binding = null; 1974 if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty()) 1975 binding = makeUnifiedBinding(valueDefn.getBinding(), valueDefn); 1976 else if (definition.hasBinding()) 1977 binding = makeUnifiedBinding(definition.getBinding(), definition); 1978 if (binding != null && !binding.isEmpty()) { 1979 if (!c.getPieces().isEmpty()) 1980 c.addPiece(gen.new Piece("binding", "br")); 1981 if (!binding.hasValueSet()) { 1982 c.getPieces().add(checkForNoChange(binding, gen.new Piece("binding", null, (context.formatPhrase(RenderingContext.GENERAL_BINDING_NO_VS_1)) + ": ", null).addStyle("font-weight:bold"))); 1983 if (binding.hasStrength()) { 1984 c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece("binding", null, " (", null))); 1985 c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece("binding", corePath + "terminologies.html#" + binding.getStrength().toCode(), egt(binding.getStrengthElement()), binding.getStrength().getDefinition()))); 1986 c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece("binding", null, ")", null))); 1987 if (binding.getStrength().testable()) { 1988 c.getPieces().add(gen.new Piece("binding", null, " ", null)); 1989 c.getPieces().add(checkForNoChange(binding, gen.new Piece("binding", null, "\u26A0", context.formatPhrase(RenderingContext.GENERAL_BINDING_NO_VS_2))).addStyle("font-weight:bold; color: #c97a18")); 1990 } 1991 } 1992 c.getPieces().add(gen.new Piece("binding", null, ": ", null)); 1993 if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) { 1994 c.addMarkdownNoPara("binding", PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()))); 1995 } else { 1996 c.addMarkdownNoPara("binding", context.formatPhrase(RenderingContext.GENERAL_BINDING_NO_DESC)); 1997 } 1998 } else { 1999 BindingResolution br = context.getPkp() == null ? makeNullBr(binding) : context.getPkp().resolveBinding(profile, binding, definition.getPath()); 2000 c.getPieces().add(checkForNoChange(binding, gen.new Piece("binding", null, (context.formatPhrase(RenderingContext.GENERAL_BINDING)) + ": ", null).addStyle("font-weight:bold"))); 2001 c.getPieces().add(checkForNoChange(binding.getValueSetElement(), checkAddExternalFlag(br, gen.new Piece("binding", br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath + br.url, br.display, br.uri)))); 2002 if (binding.hasStrength()) { 2003 c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece("binding", null, " (", null))); 2004 c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece("binding", corePath + "terminologies.html#" + binding.getStrength().toCode(), egt(binding.getStrengthElement()), binding.getStrength().getDefinition()))); 2005 c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece("binding", null, ")", null))); 2006 } 2007 if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) { 2008 c.getPieces().add(gen.new Piece("binding", null, ": ", null)); 2009 c.addMarkdownNoPara("binding", PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()))); 2010 } 2011 } 2012 AdditionalBindingsRenderer abr = new AdditionalBindingsRenderer(context.getPkp(), corePath, profile, definition.getPath(), rc, null, this); 2013 abr.seeAdditionalBindings(definition, null, false); 2014 if (binding.hasExtension(ExtensionDefinitions.EXT_MAX_VALUESET)) { 2015 abr.seeMaxBinding(ExtensionUtilities.getExtension(binding, ExtensionDefinitions.EXT_MAX_VALUESET)); 2016 } 2017 if (binding.hasExtension(ExtensionDefinitions.EXT_MIN_VALUESET)) { 2018 abr.seeMinBinding(ExtensionUtilities.getExtension(binding, ExtensionDefinitions.EXT_MIN_VALUESET)); 2019 } 2020 if (binding.hasExtension(ExtensionDefinitions.EXT_BINDING_ADDITIONAL)) { 2021 abr.seeAdditionalBindings(binding.getExtensionsByUrl(ExtensionDefinitions.EXT_BINDING_ADDITIONAL)); 2022 } 2023 abr.render(gen, c); 2024 } 2025 2026 boolean firstConstraint = true; 2027 for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { 2028// if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) { 2029 if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || (allInvariants && !isAbstractBaseProfile(inv.getSource(), profile) && !"http://hl7.org/fhir/StructureDefinition/Extension".equals(inv.getSource()) && !"http://hl7.org/fhir/StructureDefinition/Element".equals(inv.getSource()))) { 2030 if (firstConstraint) { 2031 if (!c.getPieces().isEmpty()) 2032 c.addPiece(gen.new Piece("constraint", "br")); 2033 c.addPiece(gen.new Piece("constraint", null, "Constraints: ", null)); 2034 firstConstraint = false; 2035 2036 } else 2037 c.addPiece(gen.new Piece("constraint", null, ", ", null)); 2038 c.getPieces().add(checkForNoChange(inv, gen.new Piece("constraint", null, inv.getKey(), gt(inv.getHumanElement())).addStyle("font-weight:bold"))); 2039 } 2040 } 2041 if ((definition.hasBase() && "*".equals(definition.getBase().getMax())) || (definition.hasMax() && "*".equals(definition.getMax()))) { 2042 if (c.getPieces().size() > 0) 2043 c.addPiece(gen.new Piece("br")); 2044 if (definition.hasOrderMeaning()) { 2045 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_REPEAT_ELE, definition.getOrderMeaning()), null)); 2046 } else { 2047 // don't show this, this it's important: c.getPieces().add(gen.new Piece(null, "This repeating element has no defined order", null)); 2048 } 2049 } 2050 if (definition.hasFixed()) { 2051 if (!c.getPieces().isEmpty()) { 2052 c.addPiece(gen.new Piece("br")); 2053 } 2054 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_FIXED)) + ": ", null).addStyle("font-weight:bold"))); 2055 if (!useTableForFixedValues || !allowSubRows || definition.getFixed().isPrimitive()) { 2056 String s = buildJson(definition.getFixed()); 2057 String link = null; 2058 if (Utilities.isAbsoluteUrl(s) && context.getPkp() != null) 2059 link = context.getPkp().getLinkForUrl(corePath, s); 2060 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); 2061 } else { 2062 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_AS_SHOWN), null).addStyle("color: darkgreen"))); 2063 genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false); 2064 } 2065 if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) { 2066 Piece p = describeCoded(gen, definition.getFixed()); 2067 if (p != null) 2068 c.getPieces().add(p); 2069 } 2070 } else if (definition.hasPattern()) { 2071 if (!c.getPieces().isEmpty()) { 2072 c.addPiece(gen.new Piece("br")); 2073 } 2074 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, (context.formatPhrase(RenderingContext.STRUC_DEF_REQ_PATT)) + ": ", null).addStyle("font-weight:bold"))); 2075 if (!useTableForFixedValues || !allowSubRows || definition.getPattern().isPrimitive()) 2076 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); 2077 else { 2078 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_LEAST_FOLLOW), null).addStyle("color: darkgreen"))); 2079 genFixedValue(gen, row, definition.getPattern(), snapshot, true, corePath, mustSupportOnly); 2080 } 2081 } else if (definition.hasExample()) { 2082 for (ElementDefinitionExampleComponent ex : definition.getExample()) { 2083 if (!c.getPieces().isEmpty()) { 2084 c.addPiece(gen.new Piece("br")); 2085 } 2086 c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_EXAMPLE)) + ("".equals("General") ? "" : " " + ex.getLabel()) + ": ", null).addStyle("font-weight:bold"))); 2087 c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); 2088 } 2089 } 2090 2091 ObligationsRenderer obr = new ObligationsRenderer(corePath, profile, definition.getPath(), rc, null, this, false); 2092 if (definition.hasExtension(ExtensionDefinitions.EXT_OBLIGATION_CORE, ExtensionDefinitions.EXT_OBLIGATION_TOOLS)) { 2093 obr.seeObligations(definition.getExtensionsByUrl(ExtensionDefinitions.EXT_OBLIGATION_CORE, ExtensionDefinitions.EXT_OBLIGATION_TOOLS)); 2094 } 2095 if (!definition.getPath().contains(".") && profile.hasExtension(ExtensionDefinitions.EXT_OBLIGATION_CORE, ExtensionDefinitions.EXT_OBLIGATION_TOOLS)) { 2096 obr.seeObligations(profile.getExtensionsByUrl(ExtensionDefinitions.EXT_OBLIGATION_CORE, ExtensionDefinitions.EXT_OBLIGATION_TOOLS)); 2097 } 2098 obr.renderTable(status, res, gen, c, inScopeElements); 2099 if (definition.hasMaxLength() && definition.getMaxLength() != 0) { 2100 if (!c.getPieces().isEmpty()) { 2101 c.addPiece(gen.new Piece("br")); 2102 } 2103 c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, context.formatPhrase(RenderingContext.GENERAL_MAX_LENGTH), null).addStyle("font-weight:bold"))); 2104 c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); 2105 } 2106 if (definition.hasExtension(ExtensionDefinitions.EXT_MIN_LENGTH)) { 2107 int min = ExtensionUtilities.readIntegerExtension(definition, ExtensionDefinitions.EXT_MIN_LENGTH, 0); 2108 if (min > 0) { 2109 if (!c.getPieces().isEmpty()) { 2110 c.addPiece(gen.new Piece("br")); 2111 } 2112 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.GENERAL_MIN_LENGTH), null).addStyle("font-weight:bold")); 2113 c.getPieces().add(gen.new Piece(null, Integer.toString(min), null).addStyle("color: darkgreen")); 2114 } 2115 } 2116 if (profile != null) { 2117 for (StructureDefinitionMappingComponent md : profile.getMapping()) { 2118 if (md.hasExtension(ExtensionDefinitions.EXT_TABLE_NAME)) { 2119 ElementDefinitionMappingComponent map = null; 2120 for (ElementDefinitionMappingComponent m : definition.getMapping()) 2121 if (m.getIdentity().equals(md.getIdentity())) 2122 map = m; 2123 if (map != null) { 2124 for (int i = 0; i < definition.getMapping().size(); i++) { 2125 c.addPiece(gen.new Piece("br")); 2126 c.getPieces().add(gen.new Piece(null, ExtensionUtilities.readStringExtension(md, ExtensionDefinitions.EXT_TABLE_NAME) + ": " + map.getMap(), null)); 2127 } 2128 } 2129 } 2130 } 2131 } 2132 } 2133 } 2134 2135 return c; 2136 } 2137 2138 private Set<String> determineNarrativeStatus(ElementDefinition definition, StructureDefinition profile, boolean snapshot) { 2139 Set<String> set = new HashSet<>(); 2140 try { 2141 List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, definition, !snapshot); 2142 ElementDefinition status = null; 2143 for (ElementDefinition t : children) { 2144 if (t.getName().equals("status")) { 2145 status = t; 2146 } 2147 } 2148 if (status != null && status.getBinding().hasValueSet()) { 2149 ValueSetExpansionOutcome exp = context.getContext().expandVS(ExpansionOptions.cacheNoHeirarchy().withLanguage(context.getLocale().getLanguage()).withMaxCount(-1), status.getBinding().getValueSet()); 2150 if (exp.isOk()) { 2151 for (ValueSetExpansionContainsComponent cc : exp.getValueset().getExpansion().getContains()) { 2152 set.add(cc.getCode()); 2153 } 2154 } 2155 } 2156 } catch (Exception e) { 2157 log.error("Error checking Narrative Status: "+e.getMessage(), e); 2158 } 2159 return set; 2160 } 2161 2162 private Boolean checkCanBeTarget(Extension lt, List<Extension> tc) { 2163 Boolean res = null; 2164 if (lt == null || !lt.hasValueBooleanType() || lt.getValue().hasExtension(ExtensionDefinitions.EXT_DAR)) { 2165 } else if (!lt.getValueBooleanType().hasValue()) { 2166 res = null; // GDG Dec-2024: this is true, but changed to null 2167 } else if (lt.getValueBooleanType().booleanValue()) { 2168 res = true; 2169 } else { 2170 res = false; // GDG Dec 2024- this was true, but evidently should be false, so fixed 2171 } 2172 if (res == null && !tc.isEmpty()) { 2173 res = false; 2174 for (Extension t : tc) { 2175 if (t.hasValueCodeType() && "can-be-target".equals(t.getValueCodeType().primitiveValue())) { 2176 res = true; 2177 } 2178 } 2179 } 2180 return res; 2181 } 2182 2183 private boolean isAbstractBaseProfile(String source, StructureDefinition resource) { 2184 StructureDefinition sd = context.getProfileUtilities().findProfile(source, resource); 2185 return (sd != null) && sd.getAbstract() && sd.hasUrl() && sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/"); 2186 } 2187 2188 private Piece checkAddExternalFlag(BindingResolution br, Piece piece) { 2189 if (br.external) { 2190 piece.setTagImg("external.png"); 2191 } 2192 return piece; 2193 } 2194 2195 private boolean isAttr(SourcedElementDefinition ed) { 2196 for (Enumeration<PropertyRepresentation> t : ed.getDefinition().getRepresentation()) { 2197 if (t.getValue() == PropertyRepresentation.XMLATTR) { 2198 return true; 2199 } 2200 } 2201 return false; 2202 } 2203 2204 private void getAncestorElements(List<String> inscope, StructureDefinition profile, List<SourcedElementDefinition> ancestors) { 2205 StructureDefinition base = context.getProfileUtilities().findProfile(profile.getBaseDefinition(), profile); 2206 if (base != null) { 2207 List<String> newList = Utilities.copyAdd(inscope, base.getVersionedUrl()); 2208 if (inscope.contains(base.getVersionedUrl())) { 2209 throw new FHIRException("Circular Definition detected in derivation hierarchy: "+CommaSeparatedStringBuilder.join("->", newList)); 2210 } 2211 getAncestorElements(newList, base, ancestors); 2212 for (ElementDefinition ed : base.getDifferential().getElement()) { 2213 if (Utilities.charCount(ed.getPath(), '.') == 1) { 2214 ancestors.add(new SourcedElementDefinition(base, ed)); 2215 } 2216 } 2217 } 2218 } 2219 2220 private void addCanonicalListExt(HierarchicalTableGenerator gen, Cell c, List<Extension> list, String start, boolean bold, StructureDefinition source) throws IOException { 2221 List<CanonicalType> clist = new ArrayList<>(); 2222 for (Extension ext : list) { 2223 if (ext.hasValueCanonicalType()) { 2224 clist.add(ext.getValueCanonicalType()); 2225 } 2226 } 2227 addCanonicalList(gen, c, clist, start, bold, source); 2228 } 2229 2230 private void addCanonicalList(HierarchicalTableGenerator gen, Cell c, List<CanonicalType> list, String start, boolean bold, StructureDefinition source) throws IOException { 2231 if (!list.isEmpty()) { 2232 2233 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2234 Piece p = gen.new Piece(null, start+(list.size() != 1 ? "s" : "")+" ", null); 2235 c.addPiece(p); 2236 if (bold) p.addStyle("font-weight:bold"); 2237 2238 for (int i = 0; i < list.size(); i++) { 2239 CanonicalType ct = list.get(i); 2240 if (i > 0) { 2241 if (i < list.size() - 1) { 2242 c.addPiece(gen.new Piece(null, ", ", null)); 2243 } else { 2244 c.addPiece(gen.new Piece(null, " and ", null)); 2245 } 2246 } 2247 String iu = ct.primitiveValue(); 2248 StructureDefinition sd = context.getProfileUtilities().findProfile(iu, source); 2249 if (sd == null) { 2250 sd = context.findLinkableResource(StructureDefinition.class, iu); 2251 } 2252 if (sd == null) { 2253 p = gen.new Piece(null, iu, null).addStyle("font-weight:bold"); 2254 c.addPiece(p); 2255 } else { 2256 String v = ""; 2257 if (iu.contains("|") || hasMultipleVersions(context.getContext().fetchResourceVersions(StructureDefinition.class, iu))) { 2258 v = " ("+sd.getVersion()+")"; 2259 } 2260 if (sd.hasWebPath()) { 2261 p = gen.new Piece(sd.getWebPath(), sd.present()+v, null).addStyle("font-weight:bold"); 2262 c.addPiece(p); 2263 } else { 2264 p = gen.new Piece(iu, sd.present()+v, null).addStyle("font-weight:bold"); 2265 c.addPiece(p); 2266 } 2267 } 2268 if (bold) { 2269 p.addStyle("font-weight:bold"); 2270 } 2271 } 2272 } 2273 } 2274 2275 private Piece checkForNoChange(Element source, Piece piece) { 2276 if (source.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS)) { 2277 piece.addStyle("opacity: 0.5"); 2278 } 2279 return piece; 2280 } 2281 2282 private String checkForNoChange(Element source) { 2283 if (source.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS)) { 2284 return "opacity: 0.5"; 2285 } else { 2286 return null; 2287 } 2288 } 2289 2290 private Cell genTypes(HierarchicalTableGenerator gen, Row r, ElementDefinition e, String profileBaseFileName, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean mustSupportMode, boolean diff) { 2291 Cell c = gen.new Cell(); 2292 r.getCells().add(c); 2293 if (e.hasContentReference()) { 2294 ElementInStructure ed = getElementByName(diff ? profile.getDifferential().getElement() : profile.getSnapshot().getElement(), e.getContentReference(), profile); 2295 if (ed == null) 2296 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_UNKNOWN_REF, e.getContentReference())+" ", null)); 2297 else { 2298 if (ed.getSource() == profile) { 2299 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_SEE)+" ", null)); 2300 c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), tail(ed.getElement().getPath()), ed.getElement().getPath())); 2301 } else { 2302 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_SEE)+" ", null)); 2303 c.getPieces().add(gen.new Piece(ed.getSource().getWebPath()+"#"+ed.getElement().getPath(), tail(ed.getElement().getPath())+" ("+ed.getSource().getTypeName()+")", ed.getElement().getPath())); 2304 } 2305 } 2306 return c; 2307 } 2308 List<TypeRefComponent> types = e.getType(); 2309 if (!e.hasType()) { 2310 if (root) { // we'll use base instead of types then 2311 StructureDefinition bsd = profile == null ? null : context.getProfileUtilities().findProfile(profile.getBaseDefinition(), profile); 2312 if (bsd != null) { 2313 String v = ""; 2314 if (profile != null && (profile.getBaseDefinition().contains("|") || hasMultipleVersions(context.getWorker().fetchResourceVersions(StructureDefinition.class, profile.getBaseDefinition())))) { 2315 v = v +"("+bsd.getVersion()+")"; 2316 } 2317 if (bsd.hasWebPath()) { 2318 c.getPieces().add(gen.new Piece(Utilities.isAbsoluteUrl(bsd.getWebPath()) ? bsd.getWebPath() : imagePath +bsd.getWebPath(), bsd.getName()+v, null)); 2319 } else { 2320 c.getPieces().add(gen.new Piece(null, bsd.getName()+v, null)); 2321 } 2322 } 2323 return c; 2324 } else if (e.hasContentReference()) { 2325 return c; 2326 } else { 2327 ElementDefinition d = (ElementDefinition) e.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER); 2328 if (d != null && d.hasType()) { 2329 types = new ArrayList<ElementDefinition.TypeRefComponent>(); 2330 for (TypeRefComponent tr : d.getType()) { 2331 TypeRefComponent tt = tr.copy(); 2332 tt.setUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS, true); 2333 types.add(tt); 2334 } 2335 } else { 2336 return c; 2337 } 2338 } 2339 } 2340 2341 boolean first = true; 2342 2343 TypeRefComponent tl = null; 2344 for (TypeRefComponent t : types) { 2345 if (!mustSupportMode || allTypesMustSupport(e) || isMustSupport(t)) { 2346 if (first) { 2347 first = false; 2348 } else { 2349 c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); 2350 } 2351 tl = t; 2352 if (t.hasTarget()) { 2353 if (t.hasProfile()) { 2354 String ref = t.getProfile().get(0).asStringValue(); 2355 StructureDefinition tsd = context.getProfileUtilities().findProfile(ref, profile); 2356 if (tsd != null) { 2357 // if there's multiple possible matches in scope, we will be explicit about the version 2358 if (ref.contains("|") || hasMultipleVersions(context.getContext().fetchResourceVersions(StructureDefinition.class, ref))) { 2359 c.getPieces().add(gen.new Piece(tsd.getWebPath(), tsd.getName()+"("+tsd.getVersion()+")", tsd.present())); 2360 } else { 2361 c.getPieces().add(gen.new Piece(tsd.getWebPath(), tsd.getName(), tsd.present())); 2362 } 2363 } else { 2364 c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null)); 2365 } 2366 } else { 2367 c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null)); 2368 } 2369 if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { 2370 c.addPiece(gen.new Piece(null, " ", null)); 2371 c.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_SUPP)), "S", "white", "red", null, false); 2372 } 2373 c.getPieces().add(gen.new Piece(null, "(", null)); 2374 boolean tfirst = true; 2375 for (CanonicalType u : t.getTargetProfile()) { 2376 if (!mustSupportMode || allProfilesMustSupport(t.getTargetProfile()) || isMustSupport(u)) { 2377 if (tfirst) 2378 tfirst = false; 2379 else 2380 c.addPiece(gen.new Piece(null, " | ", null)); 2381 genTargetLink(gen, profileBaseFileName, corePath, c, t, u.getValue(), null); 2382 if (!mustSupportMode && isMustSupport(u) && e.getMustSupport()) { 2383 c.addPiece(gen.new Piece(null, " ", null)); 2384 c.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_SUPP)), "S", "white", "red", null, false); 2385 } 2386 } 2387 } 2388 c.getPieces().add(gen.new Piece(null, ")", null)); 2389 if (t.getAggregation().size() > 0) { 2390 c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", " {", null)); 2391 boolean firstA = true; 2392 for (Enumeration<AggregationMode> a : t.getAggregation()) { 2393 if (firstA == true) 2394 firstA = false; 2395 else 2396 c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", ", ", null)); 2397 c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", codeForAggregation(a.getValue()), hintForAggregation(a.getValue()))); 2398 } 2399 c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", "}", null)); 2400 } 2401 } else if (t.hasProfile() && (!t.getWorkingCode().equals("Extension") || isProfiledType(t.getProfile()))) { // a profiled type 2402 String ref; 2403 boolean pfirst = true; 2404 for (CanonicalType p : t.getProfile()) { 2405 if (!mustSupportMode || allProfilesMustSupport(t.getProfile()) || isMustSupport(p)) { 2406 if (pfirst) { 2407 pfirst = false; 2408 } else { 2409 c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); 2410 } 2411 2412 ref = context.getPkp() == null ? null : context.getPkp().getLinkForProfile(profile, p.getValue()); 2413 if (ref != null) { 2414 String[] parts = ref.split("\\|"); 2415 if (parts[0].startsWith("http:") || parts[0].startsWith("https:")) { 2416 if (p.hasExtension(ExtensionDefinitions.EXT_PROFILE_ELEMENT)) { 2417 String pp = p.getExtensionString(ExtensionDefinitions.EXT_PROFILE_ELEMENT); 2418 pp = pp.substring(pp.indexOf(".")); 2419 c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1]+pp, t.getWorkingCode()))); 2420 } else { 2421 c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1], t.getWorkingCode()))); 2422 } 2423 } else { 2424 c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath+"StructureDefinition")? corePath: "")+parts[0], parts[1], t.getWorkingCode()))); 2425 } 2426 } else { 2427 c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath)? corePath: "")+ref, t.getWorkingCode(), null))); 2428 } 2429 if (!mustSupportMode && isMustSupport(p) && e.getMustSupport()) { 2430 c.addPiece(gen.new Piece(null, " ", null)); 2431 c.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_PROF_SUPP)), "S", "white", "red", null, false); 2432 } 2433 } 2434 } 2435 } else { 2436 String tc = t.getWorkingCode(); 2437 if (Utilities.isAbsoluteUrl(tc)) { 2438 StructureDefinition sd = context.getWorker().fetchTypeDefinition(tc); 2439 if (sd == null) { 2440 c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, tc), tc, null))); 2441 } else { 2442 c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, tc), sd.getTypeName(), null))); 2443 } 2444 } else if (context.getPkp() != null && context.getPkp().hasLinkFor(tc)) { 2445 c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, tc), tc, null))); 2446 } else { 2447 c.addPiece(checkForNoChange(t, gen.new Piece(null, tc, null))); 2448 } 2449 if (t.hasExtension(ExtensionDefinitions.EXT_TYPE_PARAMETER)) { 2450 c.addPiece(checkForNoChange(t, gen.new Piece(null, "<", null))); 2451 boolean pfirst = true; 2452 List<Extension> exl = t.getExtensionsByUrl(ExtensionDefinitions.EXT_TYPE_PARAMETER); 2453 for (Extension ex : exl) { 2454 if (pfirst) { pfirst = false; } else { c.addPiece(checkForNoChange(t, gen.new Piece(null, ";", null))); } 2455 if (exl.size() > 1) { 2456 c.addPiece(checkForNoChange(t, gen.new Piece(null, ex.getExtensionString("name")+": ", null))); 2457 } 2458 String type = ex.getExtensionString("type"); 2459 StructureDefinition psd = context.getContext().fetchTypeDefinition(type); 2460 if (psd == null) { 2461 c.addPiece(checkForNoChange(t, gen.new Piece(null, type, null))); 2462 } else if (psd.getWebPath() == null) { 2463 c.addPiece(checkForNoChange(t, gen.new Piece(type, psd.present(), null))); 2464 } else { 2465 c.addPiece(checkForNoChange(t, gen.new Piece(psd.getWebPath(), psd.present(), null))); 2466 } 2467 } 2468 c.addPiece(checkForNoChange(t, gen.new Piece(null, ">", null))); 2469 2470 } 2471 if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { 2472 c.addPiece(gen.new Piece(null, " ", null)); 2473 c.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_SUPP)), "S", "white", "red", null, false); 2474 } 2475 } 2476 } 2477 } 2478 return c; 2479 } 2480 2481 2482 private String typePath(String cp, StructureDefinition source) { 2483 if (source.hasUserData(UserDataNames.loader_custom_resource)) { 2484 return source.getWebPath(); 2485 } else { 2486 return pfx(cp, source.getWebPath()); 2487 } 2488 } 2489 2490 private boolean hasMultipleVersions(List<? extends CanonicalResource> list) { 2491 Set<String> vl = new HashSet<>(); 2492 for (CanonicalResource cr : list) { 2493 vl.add(cr.getVersion()); 2494 } 2495 return vl.size() > 1; 2496 } 2497 2498 private String pfx(String prefix, String url) { 2499 return Utilities.isAbsoluteUrl(url) ? url : prefix + url; 2500 } 2501 2502 private void genTargetLink(HierarchicalTableGenerator gen, String profileBaseFileName, String corePath, Cell c, TypeRefComponent t, String u, StructureDefinition src) { 2503 if (u.startsWith("http://hl7.org/fhir/StructureDefinition/")) { 2504 StructureDefinition sd = context.getProfileUtilities().findProfile(u, src); 2505 if (sd != null) { 2506 String disp = sd.hasTitle() ? sd.getTitle() : sd.getName(); 2507 c.addPiece(checkForNoChange(t, gen.new Piece(checkPrepend(corePath, sd.getWebPath()), disp, null))); 2508 } else { 2509 String rn = u.substring(40); 2510 c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, rn), rn, null))); 2511 } 2512 } else if (Utilities.isAbsoluteUrl(u)) { 2513 StructureDefinition sd = context.getProfileUtilities().findProfile(u, src); 2514 if (sd != null && context.getPkp() != null) { 2515 String v = ""; 2516 if (u.contains("|") || hasMultipleVersions(context.getWorker().fetchResourceVersions(StructureDefinition.class, u))) { 2517 v = "("+sd.getVersion()+")"; 2518 } 2519 String disp = sd.present()+v; 2520 String ref; 2521 if (u.contains("|")) { 2522 ref = sd.getWebPath(); 2523 } else { 2524 ref = context.getPkp().getLinkForProfile(null, sd.getUrl()); 2525 } 2526 if (ref != null && ref.contains("|")) 2527 ref = ref.substring(0, ref.indexOf("|")); 2528 c.addPiece(checkForNoChange(t, gen.new Piece(ref, disp, null))); 2529 } else 2530 c.addPiece(checkForNoChange(t, gen.new Piece(null, u, null))); 2531 } else if (t.hasTargetProfile() && u.startsWith("#")) 2532 c.addPiece(checkForNoChange(t, gen.new Piece(corePath+profileBaseFileName+"."+u.substring(1).toLowerCase()+".html", u, null))); 2533 } 2534 2535 private boolean isProfiledType(List<CanonicalType> theProfile) { 2536 for (CanonicalType next : theProfile){ 2537 if (StringUtils.defaultString(next.getValueAsString()).contains(":")) { 2538 return true; 2539 } 2540 } 2541 return false; 2542 } 2543 2544 2545 public String codeForAggregation(AggregationMode a) { 2546 switch (a) { 2547 case BUNDLED : return "b"; 2548 case CONTAINED : return "c"; 2549 case REFERENCED: return "r"; 2550 default: return "?"; 2551 } 2552 } 2553 2554 public String hintForAggregation(AggregationMode a) { 2555 if (a != null) 2556 return a.getDefinition(); 2557 else 2558 return null; 2559 } 2560 2561 2562 private String checkPrepend(String corePath, String path) { 2563 if (context.getPkp() != null && context.getPkp().prependLinks() && !(path.startsWith("http:") || path.startsWith("https:"))) 2564 return corePath+path; 2565 else 2566 return path; 2567 } 2568 2569 2570 private void genGridElement(String defPath, HierarchicalTableGenerator gen, List<Row> rows, ElementDefinition element, List<ElementDefinition> all, List<StructureDefinition> profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, String corePath, String imagePath, boolean root, boolean isConstraintMode, boolean diff) throws IOException, FHIRException { 2571 StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); 2572 String s = tail(element.getPath()); 2573 List<ElementDefinition> children = getChildren(all, element); 2574 boolean isExtension = (s.equals("extension") || s.equals("modifierExtension")); 2575 2576 if (!onlyInformationIsMapping(all, element)) { 2577 Row row = gen.new Row(); 2578 row.setId(context.prefixAnchor(s)); 2579 String anchor = element.getPath(); 2580 anchor = makeAnchorUnique(anchor); 2581 row.setAnchor(context.prefixAnchor(anchor)); 2582 row.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); 2583 if (element.hasSlicing()) 2584 row.setLineColor(1); 2585 else if (element.hasSliceName()) 2586 row.setLineColor(2); 2587 else 2588 row.setLineColor(0); 2589 boolean hasDef = element != null; 2590 String ref = defPath == null ? null : defPath + element.getId(); 2591 UnusedTracker used = new UnusedTracker(); 2592 used.used = true; 2593 Cell left = gen.new Cell(); 2594 if (element.getType().size() == 1 && element.getType().get(0).isPrimitive()) 2595 left.getPieces().add(gen.new Piece(ref, "\u00A0\u00A0" + s, !hasDef ? null : gt(element.getDefinitionElement())).addStyle("font-weight:bold")); 2596 else 2597 left.getPieces().add(gen.new Piece(ref, "\u00A0\u00A0" + s, !hasDef ? null : gt(element.getDefinitionElement()))); 2598 if (element.hasSliceName()) { 2599 left.getPieces().add(gen.new Piece("br")); 2600 String indent = StringUtils.repeat('\u00A0', 1+2*(element.getPath().split("\\.").length)); 2601 left.getPieces().add(gen.new Piece(null, indent + "("+element.getSliceName() + ")", null)); 2602 } 2603 row.getCells().add(left); 2604 2605 genCardinality(gen, element, row, hasDef, used, null); 2606 if (hasDef && !"0".equals(element.getMax())) 2607 genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, false, diff); 2608 else 2609 row.getCells().add(gen.new Cell()); 2610 generateGridDescription(gen, row, element, null, used.used, null, null, profile, corePath, imagePath, root, null); 2611 /* if (element.hasSlicing()) { 2612 if (standardExtensionSlicing(element)) { 2613 used.used = element.hasType() && element.getType().get(0).hasProfile(); 2614 showMissing = false; 2615 } else { 2616 row.setIcon("icon_slice.png", context.formatPhrase(RenderingContext.TEXT_ICON_SLICE); 2617 row.getCells().get(2).getPieces().clear(); 2618 for (Cell cell : row.getCells()) 2619 for (Piece p : cell.getPieces()) { 2620 p.addStyle("font-style: italic"); 2621 } 2622 } 2623 }*/ 2624 rows.add(row); 2625 for (ElementDefinition child : children) 2626 if (child.getMustSupport()) 2627 genGridElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, corePath, imagePath, false, isConstraintMode, diff); 2628 } 2629 } 2630 2631 2632 private ExtensionContext locateExtension(Class<StructureDefinition> class1, String value, StructureDefinition sd) { 2633 if (value.contains("#")) { 2634 StructureDefinition ext = context.getProfileUtilities().findProfile(value.substring(0, value.indexOf("#")), sd); 2635 if (ext == null) 2636 return null; 2637 String tail = value.substring(value.indexOf("#")+1); 2638 ElementDefinition ed = null; 2639 for (ElementDefinition ted : ext.getSnapshot().getElement()) { 2640 if (tail.equals(ted.getSliceName())) { 2641 ed = ted; 2642 return new ExtensionContext(ext, ed); 2643 } 2644 } 2645 return null; 2646 } else { 2647 StructureDefinition ext = context.getProfileUtilities().findProfile(value, sd); 2648 if (ext == null) 2649 return null; 2650 else 2651 return new ExtensionContext(ext, ext.getSnapshot().getElement().get(0)); 2652 } 2653 } 2654 2655 2656 private boolean extensionIsComplex(String value, StructureDefinition sd) { 2657 if (value.contains("#")) { 2658 StructureDefinition ext = context.getProfileUtilities().findProfile(value.substring(0, value.indexOf("#")), sd); 2659 if (ext == null) 2660 return false; 2661 String tail = value.substring(value.indexOf("#")+1); 2662 ElementDefinition ed = null; 2663 for (ElementDefinition ted : ext.getSnapshot().getElement()) { 2664 if (tail.equals(ted.getSliceName())) { 2665 ed = ted; 2666 break; 2667 } 2668 } 2669 if (ed == null) 2670 return false; 2671 int i = ext.getSnapshot().getElement().indexOf(ed); 2672 int j = i+1; 2673 while (j < ext.getSnapshot().getElement().size() && !ext.getSnapshot().getElement().get(j).getPath().equals(ed.getPath())) 2674 j++; 2675 return j - i > 5; 2676 } else { 2677 StructureDefinition ext = context.getProfileUtilities().findProfile(value, sd); 2678 return ext != null && ext.getSnapshot().getElement().size() > 5; 2679 } 2680 } 2681 2682 2683 2684 2685 private BindingResolution makeNullBr(ElementDefinitionBindingComponent binding) { 2686 BindingResolution br = new BindingResolution(); 2687 br.url = "http://none.none/none"; 2688 br.display = context.formatPhrase(RenderingContext.GENERAL_TODO); 2689 return br; 2690 } 2691 2692 private ElementDefinitionBindingComponent makeUnifiedBinding(ElementDefinitionBindingComponent binding, ElementDefinition element) { 2693 if (!element.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER)) { 2694 return binding; 2695 } 2696 ElementDefinition base = (ElementDefinition) element.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER); 2697 if (!base.hasBinding()) { 2698 return binding; 2699 } 2700 ElementDefinitionBindingComponent o = base.getBinding(); 2701 ElementDefinitionBindingComponent b = new ElementDefinitionBindingComponent(); 2702 b.setUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER, o); 2703 if (binding.hasValueSet()) { 2704 b.setValueSet(binding.getValueSet()); 2705 } else if (o.hasValueSet()) { 2706 b.setValueSet(o.getValueSet()); 2707 b.getValueSetElement().setUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS, o.getValueSetElement()); 2708 } 2709 if (binding.hasStrength()) { 2710 b.setStrength(binding.getStrength()); 2711 } else if (o.hasStrength()) { 2712 b.setStrength(o.getStrength()); 2713 b.getStrengthElement().setUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS, o.getStrengthElement()); 2714 } 2715 if (binding.hasDescription()) { 2716 b.setDescription(binding.getDescription()); 2717 } else if (o.hasDescription()) { 2718 b.setDescription(o.getDescription()); 2719 b.getDescriptionElement().setUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS, o.getDescriptionElement()); 2720 } 2721 // todo: derivation? 2722 b.getExtension().addAll(binding.getExtension()); 2723 return b; 2724 } 2725 2726 private void genFixedValue(HierarchicalTableGenerator gen, Row erow, DataType value, boolean snapshot, boolean pattern, String corePath, boolean skipnoValue) { 2727 String ref = context.getPkp().getLinkFor(corePath, value.fhirType()); 2728 if (ref != null && ref.contains(".html")) { 2729 ref = ref.substring(0, ref.indexOf(".html"))+"-definitions.html#"; 2730 } else { 2731 ref = "?gen-fv?"; 2732 } 2733 StructureDefinition sd = context.getWorker().fetchTypeDefinition(value.fhirType()); 2734 2735 for (org.hl7.fhir.r5.model.Property t : value.children()) { 2736 ElementDefinition ed = findElementDefinitionOrNull(sd, t.getName()); 2737 if (ed != null) { // might be null because of added properties across versions 2738 if (t.getValues().size() > 0 || snapshot) { 2739 if (t.getValues().size() == 0 || (t.getValues().size() == 1 && t.getValues().get(0).isEmpty())) { 2740 if (!skipnoValue) { 2741 Row row = gen.new Row(); 2742 row.setId(context.prefixAnchor(ed.getPath())); 2743 erow.getSubRows().add(row); 2744 Cell c = gen.new Cell(); 2745 row.getCells().add(c); 2746 c.addPiece(gen.new Piece((ed.getBase().getPath().equals(ed.getPath()) ? ref+ed.getPath() : corePath+(VersionUtilities.isR5Plus(context.getWorker().getVersion()) ? "types-definitions.html#"+ed.getBase().getPath() : "element-definitions.html#"+ed.getBase().getPath())), t.getName(), null)); 2747 c = gen.new Cell(); 2748 row.getCells().add(c); 2749 c.addPiece(gen.new Piece(null, null, null)); 2750 c = gen.new Cell(); 2751 row.getCells().add(c); 2752 if (!pattern) { 2753 c.addPiece(gen.new Piece(null, "0..0", null)); 2754 row.setIcon("icon_fixed.gif", context.formatPhrase(RenderingContext.STRUC_DEF_FIXED_VALUE) /*context.formatPhrase(RenderingContext.TEXT_ICON_FIXED*/); 2755 } else if (context.getContext().isPrimitiveType(t.getTypeCode())) { 2756 row.setIcon("icon_primitive.png", context.formatPhrase(RenderingContext.TEXT_ICON_PRIMITIVE)); 2757 c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); 2758 } else if (isReference(t.getTypeCode())) { 2759 row.setIcon("icon_reference.png", context.formatPhrase(RenderingContext.TEXT_ICON_REFERENCE)); 2760 c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); 2761 } else { 2762 row.setIcon("icon_datatype.gif", context.formatPhrase(RenderingContext.TEXT_ICON_DATATYPE)); 2763 c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); 2764 } 2765 c = gen.new Cell(); 2766 row.getCells().add(c); 2767 if (t.getTypeCode().contains("(")) { 2768 String tc = t.getTypeCode(); 2769 String tn = tc.substring(0, tc.indexOf("(")); 2770 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, tn), tn, null)); 2771 c.addPiece(gen.new Piece(null, "(", null)); 2772 String[] p = tc.substring(tc.indexOf("(")+1, tc.indexOf(")")).split("\\|"); 2773 for (String s : p) { 2774 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, s), s, null)); 2775 } 2776 c.addPiece(gen.new Piece(null, ")", null)); 2777 } else { 2778 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, t.getTypeCode()), t.getTypeCode(), null)); 2779 } 2780 c = gen.new Cell(); 2781 c.addPiece(gen.new Piece(null, ed.getShort(), null)); 2782 row.getCells().add(c); 2783 } 2784 } else { 2785 for (Base b : t.getValues()) { 2786 Row row = gen.new Row(); 2787 row.setId(context.prefixAnchor(ed.getPath())); 2788 erow.getSubRows().add(row); 2789 row.setIcon("icon_fixed.gif", context.formatPhrase(RenderingContext.STRUC_DEF_FIXED) /*context.formatPhrase(RenderingContext.TEXT_ICON_FIXED*/); 2790 2791 Cell c = gen.new Cell(); 2792 row.getCells().add(c); 2793 c.addPiece(gen.new Piece((ed.getBase().getPath().equals(ed.getPath()) ? ref+ed.getPath() : (VersionUtilities.isR5Ver(context.getWorker().getVersion()) ? corePath+"types-definitions.html#"+ed.getBase().getPath() : corePath+"element-definitions.html#"+ed.getBase().getPath())), t.getName(), null)); 2794 2795 c = gen.new Cell(); 2796 row.getCells().add(c); 2797 c.addPiece(gen.new Piece(null, null, null)); 2798 2799 c = gen.new Cell(); 2800 row.getCells().add(c); 2801 if (pattern) 2802 c.addPiece(gen.new Piece(null, "1.."+(t.getMaxCardinality() == 2147483647 ? "*" : Integer.toString(t.getMaxCardinality())), null)); 2803 else 2804 c.addPiece(gen.new Piece(null, "1..1", null)); 2805 2806 c = gen.new Cell(); 2807 row.getCells().add(c); 2808 if (b.fhirType().contains("(")) { 2809 String tc = b.fhirType(); 2810 String tn = tc.substring(0, tc.indexOf("(")); 2811 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, tn), tn, null)); 2812 c.addPiece(gen.new Piece(null, "(", null)); 2813 String[] p = tc.substring(tc.indexOf("(")+1, tc.indexOf(")")).split("\\|"); 2814 for (String s : p) { 2815 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, s), s, null)); 2816 } 2817 c.addPiece(gen.new Piece(null, ")", null)); 2818 } else { 2819 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, b.fhirType()), b.fhirType(), null)); 2820 } 2821 2822 if (b.isPrimitive()) { 2823 c = gen.new Cell(); 2824 row.getCells().add(c); 2825 c.addPiece(gen.new Piece(null, ed.getShort(), null)); 2826 c.addPiece(gen.new Piece("br")); 2827 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_FIXED_VALUE)+" ", null).addStyle("font-weight: bold")); 2828 String s = b.primitiveValue(); 2829 // ok. let's see if we can find a relevant link for this 2830 String link = null; 2831 if (Utilities.isAbsoluteUrl(s)) { 2832 link = context.getPkp().getLinkForUrl(corePath, s); 2833 } 2834 c.getPieces().add(gen.new Piece(link, s, null).addStyle("color: darkgreen")); 2835 } else { 2836 c = gen.new Cell(); 2837 row.getCells().add(c); 2838 c.addPiece(gen.new Piece(null, ed.getShort(), null)); 2839 c.addPiece(gen.new Piece("br")); 2840 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_FIXED_VALUE)+" ", null).addStyle("font-weight: bold")); 2841 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_COMPLEXBRACK), null).addStyle("color: darkgreen")); 2842 genFixedValue(gen, row, (DataType) b, snapshot, pattern, corePath, skipnoValue); 2843 } 2844 } 2845 } 2846 } 2847 } 2848 } 2849 } 2850 2851 2852 private ElementDefinition findElementDefinition(StructureDefinition sd, String name) { 2853 String path = sd.getTypeName()+"."+name; 2854 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 2855 if (ed.getPath().equals(path)) 2856 return ed; 2857 } 2858 throw new FHIRException(context.getWorker().formatMessage(I18nConstants.UNABLE_TO_FIND_ELEMENT_, path)); 2859 } 2860 2861 2862 private ElementDefinition findElementDefinitionOrNull(StructureDefinition sd, String name) { 2863 String path = sd.getTypeName()+"."+name; 2864 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 2865 if (ed.getPath().equals(path)) 2866 return ed; 2867 } 2868 return null; 2869 } 2870 2871 2872 private String getFixedUrl(StructureDefinition sd) { 2873 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 2874 if (ed.getPath().equals("Extension.url")) { 2875 if (ed.hasFixed() && ed.getFixed() instanceof UriType) 2876 return ed.getFixed().primitiveValue(); 2877 } 2878 } 2879 return null; 2880 } 2881 2882 2883 private Piece describeCoded(HierarchicalTableGenerator gen, DataType fixed) { 2884 if (fixed instanceof Coding) { 2885 Coding c = (Coding) fixed; 2886 ValidationResult vr = context.getWorker().validateCode(context.getTerminologyServiceOptions(), c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay()); 2887 if (vr.getDisplay() != null) 2888 return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); 2889 } else if (fixed instanceof CodeableConcept) { 2890 CodeableConcept cc = (CodeableConcept) fixed; 2891 for (Coding c : cc.getCoding()) { 2892 ValidationResult vr = context.getWorker().validateCode(context.getTerminologyServiceOptions(), c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay()); 2893 if (vr.getDisplay() != null) 2894 return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); 2895 } 2896 } 2897 return null; 2898 } 2899 2900 2901 private boolean hasDescription(DataType fixed) { 2902 if (fixed instanceof Coding) { 2903 return ((Coding) fixed).hasDisplay(); 2904 } else if (fixed instanceof CodeableConcept) { 2905 CodeableConcept cc = (CodeableConcept) fixed; 2906 if (cc.hasText()) 2907 return true; 2908 for (Coding c : cc.getCoding()) 2909 if (c.hasDisplay()) 2910 return true; 2911 } // (fixed instanceof CodeType) || (fixed instanceof Quantity); 2912 return false; 2913 } 2914 2915 2916 private boolean isCoded(DataType fixed) { 2917 return (fixed instanceof Coding) || (fixed instanceof CodeableConcept) || (fixed instanceof CodeType) || (fixed instanceof Quantity); 2918 } 2919 2920 2921 private Cell generateGridDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, ElementDefinition valueDefn) throws IOException, FHIRException { 2922 Cell c = gen.new Cell(); 2923 row.getCells().add(c); 2924 2925 if (used) { 2926 if (definition.hasContentReference()) { 2927 ElementInStructure ed = getElementByName(profile.getSnapshot().getElement(), definition.getContentReference(), profile); 2928 if (ed == null) 2929 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_UNKNOWN_REF, definition.getContentReference()), null)); 2930 else { 2931 if (ed.getSource() == profile) { 2932 c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), context.formatPhrase(RenderingContext.STRUC_DEF_SEE, ed.getElement().getPath()), null)); 2933 } else { 2934 c.getPieces().add(gen.new Piece(ed.getSource().getWebPath()+"#"+ed.getElement().getPath(), context.formatPhrase(RenderingContext.STRUC_DEF_SEE, ed.getSource().getTypeName()) +"."+ed.getElement().getPath(), null)); 2935 } 2936 } 2937 } 2938 if (definition.getPath().endsWith("url") && definition.hasFixed()) { 2939 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen"))); 2940 } else { 2941 if (url != null) { 2942 if (!c.getPieces().isEmpty()) 2943 c.addPiece(gen.new Piece("br")); 2944 String fullUrl = url.startsWith("#") ? baseURL+url : url; 2945 StructureDefinition ed = context.getProfileUtilities().findProfile(url, profile); 2946 String ref = null; 2947 if (ed != null) { 2948 String p = ed.getWebPath(); 2949 if (p != null) { 2950 ref = p.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p : Utilities.pathURL(corePath, p); 2951 } 2952 } 2953 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_URLS), null).addStyle("font-weight:bold")); 2954 c.getPieces().add(gen.new Piece(ref, fullUrl, null)); 2955 } 2956 2957 if (definition.hasSlicing()) { 2958 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2959 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_SLICES), null).addStyle("font-weight:bold")); 2960 c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); 2961 } 2962 if (definition != null) { 2963 ElementDefinitionBindingComponent binding = null; 2964 if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty()) 2965 binding = valueDefn.getBinding(); 2966 else if (definition.hasBinding()) 2967 binding = definition.getBinding(); 2968 if (binding!=null && !binding.isEmpty()) { 2969 if (!c.getPieces().isEmpty()) 2970 c.addPiece(gen.new Piece("br")); 2971 BindingResolution br = context.getPkp().resolveBinding(profile, binding, definition.getPath()); 2972 c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_BINDINGS), null).addStyle("font-weight:bold"))); 2973 c.getPieces().add(checkForNoChange(binding, checkAddExternalFlag(br, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, br.uri)))); 2974 if (binding.hasStrength()) { 2975 c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, " (", null))); 2976 c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), binding.getStrength().toCode(), binding.getStrength().getDefinition()))); 2977 c.getPieces().add(gen.new Piece(null, ")", null)); 2978 } 2979 if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) { 2980 c.getPieces().add(gen.new Piece(null, ": ", null)); 2981 c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()).asStringValue()); 2982 } 2983 if (binding.hasExtension(ExtensionDefinitions.EXT_CONCEPT_DOMAIN)) { 2984 c.getPieces().add(gen.new Piece(null, ". ", null)); 2985 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingI18nContext.SDR_CONCEPT_DOMAIN), null)); 2986 c.getPieces().add(gen.new Piece(null, ": ", null)); 2987 c.getPieces().add(describeCoded(gen, binding.getExtensionByUrl(ExtensionDefinitions.EXT_CONCEPT_DOMAIN).getValue())); 2988 } 2989 } 2990 for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { 2991 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2992 c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold"))); 2993 if (inv.getHumanElement().hasExtension(ExtensionDefinitions.EXT_REND_MD)) { 2994 c.addMarkdown(inv.getHumanElement().getExtensionString(ExtensionDefinitions.EXT_REND_MD)); 2995 } else { 2996 c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getHuman(), null))); 2997 } 2998 } 2999 if (definition.hasFixed()) { 3000 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 3001 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_FIXED_VALUE), null).addStyle("font-weight:bold"))); 3002 String s = buildJson(definition.getFixed()); 3003 String link = null; 3004 if (Utilities.isAbsoluteUrl(s)) 3005 link = context.getPkp().getLinkForUrl(corePath, s); 3006 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); 3007 } else if (definition.hasPattern()) { 3008 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 3009 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_REQUIRED_PATT), null).addStyle("font-weight:bold"))); 3010 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); 3011 } else if (definition.hasExample()) { 3012 for (ElementDefinitionExampleComponent ex : definition.getExample()) { 3013 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 3014 c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, context.formatPhrase(RenderingContext.GENERAL_EXAMPLE) +"'"+("".equals("General")? "": " "+ex.getLabel()+"'")+": ", "").addStyle("font-weight:bold"))); 3015 c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); 3016 } 3017 } 3018 if (definition.hasMaxLength() && definition.getMaxLength()!=0) { 3019 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 3020 c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, context.formatPhrase(RenderingContext.GENERAL_MAX_LENGTH), null).addStyle("font-weight:bold"))); 3021 c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); 3022 } 3023 if (definition.hasExtension(ExtensionDefinitions.EXT_MIN_LENGTH)) { 3024 int min = ExtensionUtilities.readIntegerExtension(definition, ExtensionDefinitions.EXT_MIN_LENGTH, 0); 3025 if (min > 0) { 3026 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 3027 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.GENERAL_MIN_LENGTH), null).addStyle("font-weight:bold")); 3028 c.getPieces().add(gen.new Piece(null, Integer.toString(min), null).addStyle("color: darkgreen")); 3029 } 3030 } 3031 if (profile != null) { 3032 for (StructureDefinitionMappingComponent md : profile.getMapping()) { 3033 if (md.hasExtension(ExtensionDefinitions.EXT_TABLE_NAME)) { 3034 ElementDefinitionMappingComponent map = null; 3035 for (ElementDefinitionMappingComponent m : definition.getMapping()) 3036 if (m.getIdentity().equals(md.getIdentity())) 3037 map = m; 3038 if (map != null) { 3039 for (int i = 0; i<definition.getMapping().size(); i++){ 3040 c.addPiece(gen.new Piece("br")); 3041 c.getPieces().add(gen.new Piece(null, ExtensionUtilities.readStringExtension(md, ExtensionDefinitions.EXT_TABLE_NAME)+": " + map.getMap(), null)); 3042 } 3043 } 3044 } 3045 } 3046 } 3047 if (definition.hasDefinition()) { 3048 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 3049 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.GENERAL_DEFINITION_COLON), null).addStyle("font-weight:bold")); 3050 c.addPiece(gen.new Piece("br")); 3051 c.addMarkdown(definition.getDefinition()); 3052 // c.getPieces().add(checkForNoChange(definition.getCommentElement(), gen.new Piece(null, definition.getComment(), null))); 3053 } 3054 if (definition.getComment()!=null) { 3055 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 3056 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.STRUC_DEF_COMMENT), null).addStyle("font-weight:bold")); 3057 c.addPiece(gen.new Piece("br")); 3058 c.addMarkdown(definition.getComment()); 3059 // c.getPieces().add(checkForNoChange(definition.getCommentElement(), gen.new Piece(null, definition.getComment(), null))); 3060 } 3061 } 3062 } 3063 } 3064 return c; 3065 } 3066 3067 private boolean onlyInformationIsMapping(List<ElementDefinition> list, ElementDefinition e) { 3068 return (!e.hasSliceName() && !e.hasSlicing() && (onlyInformationIsMapping(e))) && 3069 getChildren(list, e).isEmpty(); 3070 } 3071 3072 private boolean onlyInformationIsMapping(ElementDefinition d) { 3073 return !d.hasShort() && !d.hasDefinition() && 3074 !d.hasRequirements() && !d.getAlias().isEmpty() && !d.hasMinElement() && 3075 !d.hasMax() && !d.getType().isEmpty() && !d.hasContentReference() && 3076 !d.hasExample() && !d.hasFixed() && !d.hasMaxLengthElement() && 3077 !d.getCondition().isEmpty() && !d.getConstraint().isEmpty() && !d.hasMustSupportElement() && 3078 !d.hasBinding(); 3079 } 3080 3081 private boolean allAreReference(List<TypeRefComponent> types) { 3082 for (TypeRefComponent t : types) { 3083 if (!t.hasTarget()) 3084 return false; 3085 } 3086 return true; 3087 } 3088 3089 private List<ElementDefinition> getChildren(List<ElementDefinition> all, ElementDefinition element) { 3090 List<ElementDefinition> result = new ArrayList<ElementDefinition>(); 3091 int i = all.indexOf(element)+1; 3092 while (i < all.size() && all.get(i).getPath().length() > element.getPath().length()) { 3093 if ((all.get(i).getPath().substring(0, element.getPath().length()+1).equals(element.getPath()+".")) && !all.get(i).getPath().substring(element.getPath().length()+1).contains(".")) 3094 result.add(all.get(i)); 3095 i++; 3096 } 3097 return result; 3098 } 3099 3100 3101 protected String tail(String path) { 3102 if (path == null) { 3103 return ""; 3104 } else if (path.contains(".")) 3105 return path.substring(path.lastIndexOf('.')+1); 3106 else 3107 return path; 3108 } 3109 3110 private boolean slicesExist(List<ElementDefinition> elements, ElementDefinition element) { 3111 if (elements == null) { 3112 return true; 3113 } 3114 boolean found = false; 3115 int start = elements.indexOf(element); 3116 if (start < 0) { 3117 return false; 3118 } 3119 for (int i = start; i < elements.size(); i++) { 3120 ElementDefinition ed = elements.get(i); 3121 if (ed.getPath().equals(element.getPath())) { 3122 if (ed.hasSliceName()) { 3123 found = true; 3124 } 3125 } 3126 if (ed.getPath().length() < element.getPath().length()) { 3127 break; 3128 } 3129 } 3130 return found; 3131 } 3132 3133 3134 private Cell addCell(Row row, Cell cell) { 3135 row.getCells().add(cell); 3136 return (cell); 3137 } 3138 3139 private String checkAdd(String src, String app) { 3140 return app == null ? src : src + app; 3141 } 3142 3143 public static boolean hasNonBaseConditions(List<IdType> conditions) { 3144 for (IdType c : conditions) { 3145 if (!isBaseCondition(c)) { 3146 return true; 3147 } 3148 } 3149 return false; 3150 } 3151 3152 3153 public static boolean hasNonBaseConstraints(List<ElementDefinitionConstraintComponent> constraints) { 3154 for (ElementDefinitionConstraintComponent c : constraints) { 3155 if (!isBaseConstraint(c)) { 3156 return true; 3157 } 3158 } 3159 return false; 3160 } 3161 3162 public String listConstraintsAndConditions(ElementDefinition element) { 3163 Set<String> ids = new HashSet<>(); 3164 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 3165 for (ElementDefinitionConstraintComponent con : element.getConstraint()) { 3166 if (!isBaseConstraint(con)) { 3167 if (!ids.contains(con.getKey())) { 3168 ids.add(con.getKey()); 3169 b.append(con.getKey()); 3170 } 3171 } 3172 } 3173 for (IdType id : element.getCondition()) { 3174 if (!isBaseCondition(id)) { 3175 if (!ids.contains(id.asStringValue())) { 3176 ids.add(id.asStringValue()); 3177 b.append(id.asStringValue()); 3178 } 3179 } 3180 } 3181 return b.toString(); 3182 } 3183 3184 private static boolean isBaseCondition(IdType c) { 3185 String key = c.asStringValue(); 3186 return key != null && (key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-")); 3187 } 3188 3189 private static boolean isBaseConstraint(ElementDefinitionConstraintComponent con) { 3190 String key = con.getKey(); 3191 return key != null && (key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-")); 3192 } 3193 3194 private void makeChoiceRows(List<Row> subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath, String profileBaseFileName, boolean mustSupportMode, StructureDefinition src) { 3195 // create a child for each choice 3196 for (TypeRefComponent tr : element.getType()) { 3197 if (!mustSupportMode || allTypesMustSupport(element) || isMustSupport(tr)) { 3198 boolean used = false; 3199 Row choicerow = gen.new Row(); 3200 choicerow.setId(context.prefixAnchor(element.getPath())); 3201 String ts = tr.getWorkingCode(); 3202 String tu = tr.getWorkingCode(); 3203 if (Utilities.isAbsoluteUrl(ts)) { 3204 StructureDefinition sd = context.getProfileUtilities().findProfile(ts, src); 3205 if (sd != null) { 3206 ts = sd.getType(); 3207 } 3208 } 3209 if (Utilities.isAbsoluteUrl(ts)) { 3210 ts = utail(ts); 3211 } 3212 if (isReference(tu)) { 3213 used = true; 3214 choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(ts)), null, null)); 3215 choicerow.getCells().add(gen.new Cell()); 3216 choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); 3217 choicerow.setIcon("icon_reference.png", context.formatPhrase(RenderingContext.TEXT_ICON_REFERENCE)); 3218 Cell c = gen.new Cell(); 3219 choicerow.getCells().add(c); 3220 if (ADD_REFERENCE_TO_TABLE) { 3221 if (tr.getWorkingCode().equals("canonical")) 3222 c.getPieces().add(gen.new Piece(corePath+"datatypes.html#canonical", "canonical", null)); 3223 else 3224 c.getPieces().add(gen.new Piece(corePath+"references.html#Reference", "Reference", null)); 3225 if (!mustSupportMode && isMustSupportDirect(tr) && element.getMustSupport()) { 3226 c.addPiece(gen.new Piece(null, " ", null)); 3227 c.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_SUPP)), "S", "white", "red", null, false); 3228 } 3229 c.getPieces().add(gen.new Piece(null, "(", null)); 3230 } 3231 boolean first = true; 3232 for (CanonicalType rt : tr.getTargetProfile()) { 3233 if (!mustSupportMode || allProfilesMustSupport(tr.getTargetProfile()) || isMustSupport(rt)) { 3234 if (!first) 3235 c.getPieces().add(gen.new Piece(null, " | ", null)); 3236 genTargetLink(gen, profileBaseFileName, corePath, c, tr, rt.getValue(), src); 3237 if (!mustSupportMode && isMustSupport(rt) && element.getMustSupport()) { 3238 c.addPiece(gen.new Piece(null, " ", null)); 3239 c.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_TARG_SUPP)), "S", "white", "red", null, false); 3240 } 3241 first = false; 3242 } 3243 } 3244 if (first) { 3245 c.getPieces().add(gen.new Piece(null, "Any", null)); 3246 } 3247 3248 if (ADD_REFERENCE_TO_TABLE) { 3249 c.getPieces().add(gen.new Piece(null, ")", null)); 3250 } 3251 3252 } else { 3253 StructureDefinition sd = context.getWorker().fetchTypeDefinition(tu); 3254 if (sd == null) { 3255 3256 log.debug("Unable to find "+tu); 3257 3258 sd = context.getWorker().fetchTypeDefinition(tu); 3259 } else if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { 3260 used = true; 3261 choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(ts)), sd.getDescription(), null)); 3262 choicerow.getCells().add(gen.new Cell()); 3263 choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); 3264 choicerow.setIcon("icon_primitive.png", context.formatPhrase(RenderingContext.TEXT_ICON_PRIMITIVE)); 3265 Cell c = gen.new Cell(null, corePath+"datatypes.html#"+tu, sd.getTypeName(), null, null); 3266 choicerow.getCells().add(c); 3267 if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { 3268 c.addPiece(gen.new Piece(null, " ", null)); 3269 c.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_TARG_SUPP)), "S", "white", "red", null, false); 3270 } 3271 } else { 3272 used = true; 3273 choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(ts)), sd.getDescription(), null)); 3274 choicerow.getCells().add(gen.new Cell()); 3275 choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); 3276 choicerow.setIcon("icon_datatype.gif", context.formatPhrase(RenderingContext.TEXT_ICON_DATATYPE)); 3277 Cell c = gen.new Cell(null, context.getPkp().getLinkFor(corePath, tu), sd.getTypeName(), null, null); 3278 choicerow.getCells().add(c); 3279 if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { 3280 c.addPiece(gen.new Piece(null, " ", null)); 3281 c.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_SUPP)), "S", "white", "red", null, false); 3282 } 3283 } 3284 if (tr.hasProfile() && used) { 3285 Cell typeCell = choicerow.getCells().get(3); 3286 typeCell.addPiece(gen.new Piece(null, "(", null)); 3287 boolean first = true; 3288 for (CanonicalType pt : tr.getProfile()) { 3289 if (!mustSupportMode || allProfilesMustSupport(tr.getProfile()) || isMustSupport(pt)) { 3290 if (first) first = false; else typeCell.addPiece(gen.new Piece(null, " | ", null)); 3291 StructureDefinition psd = context.getProfileUtilities().findProfile(pt.getValue(), src); 3292 if (psd == null) 3293 typeCell.addPiece(gen.new Piece(null, "?gen-e2?", null)); 3294 else 3295 typeCell.addPiece(gen.new Piece(psd.getWebPath(), psd.getName(), psd.present())); 3296 if (!mustSupportMode && isMustSupport(pt) && element.getMustSupport()) { 3297 typeCell.addPiece(gen.new Piece(null, " ", null)); 3298 typeCell.addStyledText((context.formatPhrase(RenderingContext.STRUC_DEF_PROF_SUPP)), "S", "white", "red", null, false); 3299 } 3300 } 3301 } 3302 typeCell.addPiece(gen.new Piece(null, ")", null)); 3303 } 3304 } 3305 if (used) { 3306 choicerow.getCells().add(gen.new Cell()); 3307 subRows.add(choicerow); 3308 } 3309 } 3310 } 3311 } 3312 3313 private boolean isReference(String t) { 3314 return t.equals("Reference") || t.equals("canonical"); 3315 } 3316 3317 3318 3319 private List<ElementChoiceGroup> readChoices(ElementDefinition ed, List<ElementDefinition> children) { 3320 List<ElementChoiceGroup> result = new ArrayList<>(); 3321 for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { 3322 ElementChoiceGroup grp = context.getProfileUtilities().processConstraint(children, c); 3323 if (grp != null) { 3324 result.add(grp); 3325 } 3326 } 3327 return result; 3328 } 3329 3330 private Piece checkForNoChange(Element src1, Element src2, Piece piece) { 3331 if (src1.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS) && src2.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS)) { 3332 piece.addStyle("opacity: 0.5"); 3333 } 3334 return piece; 3335 } 3336 3337 3338 private String buildJson(DataType value) throws IOException { 3339 if (value instanceof PrimitiveType) 3340 return ((PrimitiveType<?>) value).asStringValue(); 3341 3342 IParser json = new JsonParser(); 3343 return json.composeString(value, null); 3344 } 3345 3346 private String describeSlice(ElementDefinitionSlicingComponent slicing) { 3347 return formatPhrase(RenderingContext.SD_SLICING_INFO, slicing.getOrdered() ? (context.formatPhrase(RenderingContext.STRUC_DEF_ORDERED)) : (context.formatPhrase(RenderingContext.STRUC_DEF_UNORDERED)), describe(slicing.getRules()), commas(slicing.getDiscriminator())); 3348 } 3349 3350 3351 3352 private String commas(List<ElementDefinitionSlicingDiscriminatorComponent> list) { 3353 CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder(); 3354 for (ElementDefinitionSlicingDiscriminatorComponent id : list) 3355 c.append((id.hasType() ? id.getType().toCode() : "??")+":"+id.getPath()); 3356 return c.toString(); 3357 } 3358 3359 3360 private String describe(SlicingRules rules) { 3361 if (rules == null) 3362 return (context.formatPhrase(RenderingContext.STRUC_DEF_UNSPECIFIED)); 3363 switch (rules) { 3364 case CLOSED : return (context.formatPhrase(RenderingContext.STRUC_DEF_CLOSED)); 3365 case OPEN : return (context.formatPhrase(RenderingContext.STRUC_DEF_OPEN)); 3366 case OPENATEND : return (context.formatPhrase(RenderingContext.STRUC_DEF_OPEN_END)); 3367 default: 3368 return "?gen-sr?"; 3369 } 3370 } 3371 3372 private boolean allTypesMustSupport(ElementDefinition e) { 3373 boolean all = true; 3374 boolean any = false; 3375 for (TypeRefComponent tr : e.getType()) { 3376 all = all && isMustSupport(tr); 3377 any = any || isMustSupport(tr); 3378 } 3379 return !all && !any; 3380 } 3381 3382 private boolean allProfilesMustSupport(List<CanonicalType> profiles) { 3383 boolean all = true; 3384 boolean any = false; 3385 for (CanonicalType u : profiles) { 3386 all = all && isMustSupport(u); 3387 any = any || isMustSupport(u); 3388 } 3389 return !all && !any; 3390 } 3391 public boolean isMustSupportDirect(TypeRefComponent tr) { 3392 return ("true".equals(ExtensionUtilities.readStringExtension(tr, ExtensionDefinitions.EXT_MUST_SUPPORT))); 3393 } 3394 3395 public boolean isMustSupport(TypeRefComponent tr) { 3396 if ("true".equals(ExtensionUtilities.readStringExtension(tr, ExtensionDefinitions.EXT_MUST_SUPPORT))) { 3397 return true; 3398 } 3399 if (isMustSupport(tr.getProfile())) { 3400 return true; 3401 } 3402 return isMustSupport(tr.getTargetProfile()); 3403 } 3404 3405 public boolean isMustSupport(List<CanonicalType> profiles) { 3406 for (CanonicalType ct : profiles) { 3407 if (isMustSupport(ct)) { 3408 return true; 3409 } 3410 } 3411 return false; 3412 } 3413 3414 3415 public boolean isMustSupport(CanonicalType profile) { 3416 return "true".equals(ExtensionUtilities.readStringExtension(profile, ExtensionDefinitions.EXT_MUST_SUPPORT)); 3417 } 3418 3419 3420 3421 private SpanEntry buildSpanEntryFromProfile(String name, String cardinality, StructureDefinition profile) throws IOException { 3422 SpanEntry res = new SpanEntry(); 3423 res.setName(name); 3424 res.setCardinality(cardinality); 3425 res.setProfileLink(profile.getWebPath()); 3426 res.setResType(profile.getTypeName()); 3427 StructureDefinition base = context.getProfileUtilities().findProfile(res.getResType(), profile); 3428 if (base != null) 3429 res.setResLink(base.getWebPath()); 3430 res.setId(context.prefixAnchor(profile.getId())); 3431 res.setProfile(profile.getDerivation() == TypeDerivationRule.CONSTRAINT); 3432 StringBuilder b = new StringBuilder(); 3433 b.append(res.getResType()); 3434 boolean first = true; 3435 boolean open = false; 3436 if (profile.getDerivation() == TypeDerivationRule.CONSTRAINT) { 3437 res.setDescription(profile.getName()); 3438 for (ElementDefinition ed : profile.getSnapshot().getElement()) { 3439 if (isKeyProperty(ed.getBase().getPath()) && ed.hasFixed()) { 3440 if (first) { 3441 open = true; 3442 first = false; 3443 b.append("["); 3444 } else { 3445 b.append(", "); 3446 } 3447 b.append(tail(ed.getBase().getPath())); 3448 b.append("="); 3449 b.append(summarize(ed.getFixed())); 3450 } 3451 } 3452 if (open) 3453 b.append("]"); 3454 } else 3455 res.setDescription(context.formatPhrase(RenderingContext.STRUC_DEF_FHIR, profile.getName())+" "); 3456 res.setType(b.toString()); 3457 return res ; 3458 } 3459 3460 3461 private String summarize(DataType value) throws IOException { 3462 if (value instanceof Coding) 3463 return summarizeCoding((Coding) value); 3464 else if (value instanceof CodeableConcept) 3465 return summarizeCodeableConcept((CodeableConcept) value); 3466 else 3467 return buildJson(value); 3468 } 3469 3470 3471 private String summarizeCoding(Coding value) { 3472 String uri = value.getSystem(); 3473 String system = displaySystem(uri); 3474 if (Utilities.isURL(system)) { 3475 if (system.equals("http://cap.org/protocols")) 3476 system = context.formatPhrase(RenderingContext.STRUC_DEF_CAP); 3477 } 3478 return system+" "+value.getCode(); 3479 } 3480 3481 3482 private String summarizeCodeableConcept(CodeableConcept value) { 3483 if (value.hasCoding()) 3484 return summarizeCoding(value.getCodingFirstRep()); 3485 else 3486 return value.getText(); 3487 } 3488 3489 3490 private boolean isKeyProperty(String path) { 3491 return Utilities.existsInList(path, "Observation.code"); 3492 } 3493 3494 3495 private TableModel initSpanningTable(HierarchicalTableGenerator gen, String prefix, boolean isLogical, String id) throws IOException { 3496 TableModel model = gen.new TableModel(id, true); 3497 3498 if (context.getRules() == GenerationRules.VALID_RESOURCE || context.isInlineGraphics()) { 3499 model.setDocoImg(HierarchicalTableGenerator.help16AsData()); 3500 } else { 3501 model.setDocoImg(Utilities.pathURL(prefix, "help16.png")); 3502 } 3503 model.setDocoRef(Utilities.pathURL(prefix, "formats.html#table")); // todo: change to graph definition 3504 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.STRUC_DEF_PROPERTY), context.formatPhrase(RenderingContext.STRUC_DEF_PROF_RES), null, 0)); 3505 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.GENERAL_CARD), context.formatPhrase(RenderingContext.STRUC_DEF_MAX_MIN), null, 0)); 3506 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.GENERAL_CONTENT), context.formatPhrase(RenderingContext.STRUC_DEF_WHAT), null, 0)); 3507 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.GENERAL_DESC), context.formatPhrase(RenderingContext.STRUC_DEF_DESC_PROF), null, 0)); 3508 return model; 3509 } 3510 3511 private void genSpanEntry(HierarchicalTableGenerator gen, List<Row> rows, SpanEntry span) throws IOException { 3512 Row row = gen.new Row(); 3513 row.setId(context.prefixAnchor("??")); 3514 rows.add(row); 3515 row.setAnchor(context.prefixAnchor(span.getId())); 3516 //row.setColor(..?); 3517 if (span.isProfile()) { 3518 row.setIcon("icon_profile.png", context.formatPhrase(RenderingContext.GENERAL_PROF)); 3519 } else { 3520 row.setIcon("icon_resource.png", context.formatPhrase(RenderingContext.GENERAL_RESOURCE)); 3521 } 3522 3523 row.getCells().add(gen.new Cell(null, null, span.getName(), null, null)); 3524 row.getCells().add(gen.new Cell(null, null, span.getCardinality(), null, null)); 3525 row.getCells().add(gen.new Cell(null, span.getProfileLink(), span.getType(), null, null)); 3526 row.getCells().add(gen.new Cell(null, null, span.getDescription(), null, null)); 3527 3528 for (SpanEntry child : span.getChildren()) { 3529 genSpanEntry(gen, row.getSubRows(), child); 3530 } 3531 } 3532 3533 3534 public XhtmlNode generateSpanningTable(StructureDefinition profile, String imageFolder, boolean onlyConstraints, String constraintPrefix, Set<String> outputTracker, String anchorPrefix) throws IOException, FHIRException { 3535 anchors.clear(); 3536 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, false, true, "", anchorPrefix); 3537 TableModel model = initSpanningTable(gen, "", false, profile.getId()); 3538 Set<String> processed = new HashSet<String>(); 3539 SpanEntry span = buildSpanningTable("(focus)", "", profile, processed, onlyConstraints, constraintPrefix); 3540 3541 genSpanEntry(gen, model.getRows(), span); 3542 return gen.generate(model, "", 0, outputTracker); 3543 } 3544 3545 private SpanEntry buildSpanningTable(String name, String cardinality, StructureDefinition profile, Set<String> processed, boolean onlyConstraints, String constraintPrefix) throws IOException { 3546 SpanEntry res = buildSpanEntryFromProfile(name, cardinality, profile); 3547 boolean wantProcess = !processed.contains(profile.getUrl()); 3548 processed.add(profile.getUrl()); 3549 if (wantProcess && profile.getDerivation() == TypeDerivationRule.CONSTRAINT) { 3550 for (ElementDefinition ed : profile.getSnapshot().getElement()) { 3551 if (!"0".equals(ed.getMax()) && ed.getType().size() > 0) { 3552 String card = getCardinality(ed, profile.getSnapshot().getElement()); 3553 if (!card.endsWith(".0")) { 3554 List<String> refProfiles = listReferenceProfiles(ed); 3555 if (refProfiles.size() > 0) { 3556 String uri = refProfiles.get(0); 3557 if (uri != null) { 3558 StructureDefinition sd = context.getProfileUtilities().findProfile(uri, profile); 3559 if (sd != null && (!onlyConstraints || (sd.getDerivation() == TypeDerivationRule.CONSTRAINT && (constraintPrefix == null || sd.getUrl().startsWith(constraintPrefix))))) { 3560 res.getChildren().add(buildSpanningTable(nameForElement(ed), card, sd, processed, onlyConstraints, constraintPrefix)); 3561 } 3562 } 3563 } 3564 } 3565 } 3566 } 3567 } 3568 return res; 3569 } 3570 3571 3572 private String getCardinality(ElementDefinition ed, List<ElementDefinition> list) { 3573 int min = ed.getMin(); 3574 int max = !ed.hasMax() || ed.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(ed.getMax()); 3575 ElementDefinition ned = ed; 3576 while (ned != null && ned.getPath().contains(".")) { 3577 ned = findParent(ned, list); 3578 if (ned != null) { // todo: this can happen if we've walked into a resoruce. Not sure what to about that? 3579 if ("0".equals(ned.getMax())) 3580 max = 0; 3581 else if (!ned.getMax().equals("1") && !ned.hasSlicing()) 3582 max = Integer.MAX_VALUE; 3583 if (ned.getMin() == 0) { 3584 min = 0; 3585 } 3586 } 3587 } 3588 return Integer.toString(min)+".."+(max == Integer.MAX_VALUE ? "*" : Integer.toString(max)); 3589 } 3590 3591 3592 private ElementDefinition findParent(ElementDefinition ed, List<ElementDefinition> list) { 3593 int i = list.indexOf(ed)-1; 3594 while (i >= 0 && !ed.getPath().startsWith(list.get(i).getPath()+".")) 3595 i--; 3596 if (i == -1) 3597 return null; 3598 else 3599 return list.get(i); 3600 } 3601 3602 3603 private List<String> listReferenceProfiles(ElementDefinition ed) { 3604 List<String> res = new ArrayList<String>(); 3605 for (TypeRefComponent tr : ed.getType()) { 3606 // code is null if we're dealing with "value" and profile is null if we just have Reference() 3607 if (tr.hasTarget() && tr.hasTargetProfile()) 3608 for (UriType u : tr.getTargetProfile()) 3609 res.add(u.getValue()); 3610 } 3611 return res; 3612 } 3613 3614 3615 private String nameForElement(ElementDefinition ed) { 3616 return ed.getPath().substring(ed.getPath().indexOf(".")+1); 3617 } 3618 3619 public XhtmlNode formatTypeSpecifiers(ElementDefinition d) { 3620 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 3621 boolean first = true; 3622 for (Extension e : d.getExtensionsByUrl(ExtensionDefinitions.EXT_TYPE_SPEC)) { 3623 if (first) first = false; else x.br(); 3624 String cond = ExtensionUtilities.readStringExtension(e, "condition"); 3625 String type = ExtensionUtilities.readStringExtension(e, "type"); 3626 x.tx(context.formatPhrase(RenderingContext.STRUC_DEF_IF)+" "); 3627 x.code().tx(cond); 3628 x.tx(" "+(context.formatPhrase(RenderingContext.STRUC_DEF_THEN_TYPE)+" ")); 3629 StructureDefinition sd = context.getContext().fetchTypeDefinition(type); 3630 if (sd == null) { 3631 x.code().tx(type); 3632 } else { 3633 x.ah(context.prefixLocalHref(sd.getWebPath())).tx(sd.getTypeName()); 3634 } 3635 } 3636 return first ? null : x; 3637 } 3638 3639 public XhtmlNode generateExtensionTable(RenderingStatus status, String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set<String> outputTracker, RenderingContext rc, String defPath, String anchorPrefix, ResourceWrapper res, boolean diff) throws IOException, FHIRException { 3640 anchors.clear(); 3641 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true, defPath, anchorPrefix); 3642 TableModel model = gen.initNormalTable(corePath, false, true, ed.getId()+(full ? "f" : "n"), true, TableGenerationMode.XHTML); 3643 3644 boolean deep = false; 3645 String m = ""; 3646 boolean vdeep = false; 3647 if (ed.getSnapshot().getElementFirstRep().getIsModifier()) 3648 m = "modifier_"; 3649 for (ElementDefinition eld : ed.getSnapshot().getElement()) { 3650 deep = deep || eld.getPath().contains("Extension.extension."); 3651 vdeep = vdeep || eld.getPath().contains("Extension.extension.extension."); 3652 } 3653 Row r = gen.new Row(); 3654 r.setId(context.prefixAnchor("Extension")); 3655 model.getRows().add(r); 3656 String en; 3657 if (!full) 3658 en = ed.getName(); 3659 else if (ed.getSnapshot().getElement().get(0).getIsModifier()) 3660 en = "modifierExtension"; 3661 else 3662 en = "extension"; 3663 3664 r.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#extension."+ed.getName(), en, null, null)); 3665 r.getCells().add(gen.new Cell()); 3666 r.getCells().add(gen.new Cell(null, null, describeCardinality(ed.getSnapshot().getElement().get(0), null, new UnusedTracker()), null, null)); 3667 3668 ElementDefinition ved = null; 3669 if (full || vdeep) { 3670 r.getCells().add(gen.new Cell("", "", "Extension", null, null)); 3671 3672 r.setIcon(deep ? "icon_"+m+"extension_complex.png" : "icon_extension_simple.png", deep ? context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_COMPLEX) : context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_SIMPLE)); 3673 List<ElementDefinition> children = getChildren(ed.getSnapshot().getElement(), ed.getSnapshot().getElement().get(0)); 3674 for (ElementDefinition child : children) 3675 if (!child.getPath().endsWith(".id")) { 3676 List<StructureDefinition> sdl = new ArrayList<>(); 3677 sdl.add(ed); 3678 genElement(status, defFile == null ? "" : defFile+"-definitions.html#extension.", gen, r.getSubRows(), child, ed.getSnapshot().getElement(), sdl, true, defFile, true, full, corePath, imagePath, true, false, false, false, null, false, rc, "", ed, null, res, false, null, diff); 3679 } 3680 } else if (deep) { 3681 List<ElementDefinition> children = new ArrayList<ElementDefinition>(); 3682 for (ElementDefinition ted : ed.getSnapshot().getElement()) { 3683 if (ted.getPath().equals("Extension.extension")) 3684 children.add(ted); 3685 } 3686 3687 r.getCells().add(gen.new Cell("", "", "Extension", null, null)); 3688 r.setIcon("icon_"+m+"extension_complex.png", context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_COMPLEX)); 3689 3690 for (ElementDefinition c : children) { 3691 ved = getValueFor(ed, c); 3692 ElementDefinition ued = getUrlFor(ed, c); 3693 if (ved != null && ued != null) { 3694 Row r1 = gen.new Row(); 3695 r1.setId(context.prefixAnchor(ued.getPath())); 3696 r.getSubRows().add(r1); 3697 r1.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#"+ed.getId()+"."+c.getId(), ((UriType) ued.getFixed()).getValue(), null, null)); 3698 r1.getCells().add(gen.new Cell()); 3699 r1.getCells().add(gen.new Cell(null, null, describeCardinality(c, null, new UnusedTracker()), null, null)); 3700 genTypes(gen, r1, ved, defFile, ed, corePath, imagePath, false, false, diff); 3701 r1.setIcon("icon_"+m+"extension_simple.png", context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_SIMPLE)); 3702 generateDescription(status, gen, r1, c, null, corePath, corePath, ed, corePath, imagePath, false, false, false, ved, false, false, false, rc, new ArrayList<ElementDefinition>(), res); 3703 } 3704 } 3705 } else { 3706 for (ElementDefinition ted : ed.getSnapshot().getElement()) { 3707 if (ted.getPath().startsWith("Extension.value")) 3708 ved = ted; 3709 } 3710 3711 genTypes(gen, r, ved, defFile, ed, corePath, imagePath, false, false, diff); 3712 3713 r.setIcon("icon_"+m+"extension_simple.png", context.formatPhrase(RenderingContext.TEXT_ICON_EXTENSION_SIMPLE)); 3714 } 3715 Cell c = gen.new Cell("", "", "URL = "+ed.getUrl(), null, null); 3716 Piece cc = gen.new Piece(null, ed.getName()+": ", null); 3717 c.addPiece(gen.new Piece("br")).addPiece(cc); 3718 c.addMarkdown(ed.getDescription()); 3719 3720 if (!full && !(deep || vdeep) && ved != null && ved.hasBinding()) { 3721 c.addPiece(gen.new Piece("br")); 3722 BindingResolution br = context.getPkp().resolveBinding(ed, ved.getBinding(), ved.getPath()); 3723 c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_BINDING))+": ", null).addStyle("font-weight:bold"))); 3724 c.getPieces().add(checkForNoChange(ved.getBinding(), checkAddExternalFlag(br, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, br.uri)))); 3725 if (ved.getBinding().hasStrength()) { 3726 c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, " (", null))); 3727 c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(corePath+"terminologies.html#"+ved.getBinding().getStrength().toCode(), egt(ved.getBinding().getStrengthElement()), ved.getBinding().getStrength().getDefinition()))); 3728 c.getPieces().add(gen.new Piece(null, ")", null)); 3729 } 3730 if (ved.getBinding().hasDescription() && MarkDownProcessor.isSimpleMarkdown(ved.getBinding().getDescription())) { 3731 c.getPieces().add(gen.new Piece(null, ": ", null)); 3732 c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context.getWorker(), ved.getBinding().getDescriptionElement()).asStringValue()); 3733 } 3734 3735 if (ved.getBinding().hasExtension(ExtensionDefinitions.EXT_CONCEPT_DOMAIN)) { 3736 c.getPieces().add(gen.new Piece(null, ". ", null)); 3737 c.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingI18nContext.SDR_CONCEPT_DOMAIN), null)); 3738 c.getPieces().add(gen.new Piece(null, ": ", null)); 3739 c.getPieces().add(describeCoded(gen, ved.getBinding().getExtensionByUrl(ExtensionDefinitions.EXT_CONCEPT_DOMAIN).getValue())); 3740 } 3741 } 3742 c.addPiece(gen.new Piece("br")).addPiece(gen.new Piece(null, ProfileUtilities.describeExtensionContext(ed), null)); 3743 r.getCells().add(c); 3744 3745 try { 3746 return gen.generate(model, corePath, 0, outputTracker); 3747 } catch (org.hl7.fhir.exceptions.FHIRException e) { 3748 throw new FHIRException(e.getMessage(), e); 3749 } 3750 } 3751 3752 private String describeCardinality(ElementDefinition definition, ElementDefinition fallback, UnusedTracker tracker) { 3753 IntegerType min = definition.hasMinElement() ? definition.getMinElement() : new IntegerType(); 3754 StringType max = definition.hasMaxElement() ? definition.getMaxElement() : new StringType(); 3755 if (min.isEmpty() && fallback != null) 3756 min = fallback.getMinElement(); 3757 if (max.isEmpty() && fallback != null) 3758 max = fallback.getMaxElement(); 3759 3760 tracker.used = !max.isEmpty() && !max.getValue().equals("0"); 3761 3762 if (min.isEmpty() && max.isEmpty()) 3763 return null; 3764 else 3765 return (!min.hasValue() ? "" : Integer.toString(min.getValue())) + ".." + (!max.hasValue() ? "" : max.getValue()); 3766 } 3767 3768 3769 private ElementDefinition getValueFor(StructureDefinition ed, ElementDefinition c) { 3770 int i = ed.getSnapshot().getElement().indexOf(c) + 1; 3771 while (i < ed.getSnapshot().getElement().size() && ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".")) { 3772 if (ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".value")) 3773 return ed.getSnapshot().getElement().get(i); 3774 i++; 3775 } 3776 return null; 3777 } 3778 3779 private ElementDefinition getUrlFor(StructureDefinition ed, ElementDefinition c) { 3780 int i = ed.getSnapshot().getElement().indexOf(c) + 1; 3781 while (i < ed.getSnapshot().getElement().size() && ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".")) { 3782 if (ed.getSnapshot().getElement().get(i).getPath().equals(c.getPath()+".url")) 3783 return ed.getSnapshot().getElement().get(i); 3784 i++; 3785 } 3786 return null; 3787 } 3788 3789 public void renderDict(RenderingStatus status, StructureDefinition sd, List<ElementDefinition> elements, XhtmlNode t, boolean incProfiledOut, int mode, String anchorPrefix, ResourceWrapper res) throws FHIRException, IOException { 3790 int i = 0; 3791 Map<String, ElementDefinition> allAnchors = new HashMap<>(); 3792 List<ElementDefinition> excluded = new ArrayList<>(); 3793 List<ElementDefinition> stack = new ArrayList<>(); // keeps track of parents, for anchor generation 3794 3795 for (ElementDefinition ec : elements) { 3796 addToStack(stack, ec); 3797 generateAnchors(stack, allAnchors); 3798 checkInScope(stack, excluded); 3799 } 3800 Stack<ElementDefinition> dstack = new Stack<>(); 3801 for (ElementDefinition ec : elements) { 3802 if ((incProfiledOut || !"0".equals(ec.getMax())) && !excluded.contains(ec)) { 3803 ElementDefinition compareElement = null; 3804 if (mode==GEN_MODE_DIFF) 3805 compareElement = getBaseElement(ec, sd.getBaseDefinition(), sd); 3806 else if (mode==GEN_MODE_KEY) 3807 compareElement = getRootElement(ec, sd); 3808 3809 List<String> anchors = makeAnchors(ec, anchorPrefix); 3810 String title = ec.getId(); 3811 XhtmlNode tr = t.tr(); 3812 XhtmlNode sp = renderStatus(ec, tr.td("structure").colspan(2).spanClss("self-link-parent")); 3813 for (String s : anchors) { 3814 sp.an(context.prefixAnchor(s)).tx(" "); 3815 } 3816 sp.span("color: grey", null).tx(Integer.toString(i++)); 3817 sp.b().tx(". "+title); 3818 link(sp, ec.getId(), anchorPrefix); 3819 if (isProfiledExtension(ec)) { 3820 StructureDefinition extDefn = context.getProfileUtilities().findProfile(ec.getType().get(0).getProfile().get(0).getValue(), sd); 3821 if (extDefn == null) { 3822 generateElementInner(status, t, sd, ec, 1, null, compareElement, null, false, "", anchorPrefix, elements, res); 3823 } else { 3824 ElementDefinition valueDefn = getExtensionValueDefinition(extDefn); 3825 ElementDefinition compareValueDefn = null; 3826 try { 3827 StructureDefinition compareExtDefn = context.getProfileUtilities().findProfile(compareElement.getType().get(0).getProfile().get(0).getValue(), sd); 3828 compareValueDefn = getExtensionValueDefinition(extDefn); 3829 } catch (Exception except) {} 3830 generateElementInner(status, t, sd, ec, valueDefn == null || valueDefn.prohibited() ? 2 : 3, valueDefn, compareElement, compareValueDefn, false, "", anchorPrefix, elements, res); 3831 // generateElementInner(b, extDefn, extDefn.getSnapshot().getElement().get(0), valueDefn == null ? 2 : 3, valueDefn); 3832 } 3833 } else { 3834 while (!dstack.isEmpty() && !isParent(dstack.peek(), ec)) { 3835 finish(status, t, sd, dstack.pop(), mode, "", anchorPrefix, res); 3836 } 3837 dstack.push(ec); 3838 generateElementInner(status, t, sd, ec, mode, null, compareElement, null, false, "", anchorPrefix, elements, res); 3839 if (ec.hasSlicing()) { 3840 generateSlicing(t, sd, ec, ec.getSlicing(), compareElement, mode, false); 3841 } 3842 } 3843 } 3844 t.tx("\r\n"); 3845 i++; 3846 } 3847 while (!dstack.isEmpty()) { 3848 finish(status, t, sd, dstack.pop(), mode, "", anchorPrefix, res); 3849 } 3850 finish(status, t, sd, null, mode, "", anchorPrefix, res); 3851 } 3852 3853 private void finish(RenderingStatus status, XhtmlNode t, StructureDefinition sd, ElementDefinition ed, int mode, String defPath, String anchorPrefix, ResourceWrapper res) throws FHIRException, IOException { 3854 for (Base b : VersionComparisonAnnotation.getDeleted(ed == null ? sd : ed, "element")) { 3855 ElementDefinition ec = (ElementDefinition) b; 3856 String title = ec.getId(); 3857 XhtmlNode tr = t.tr(); 3858 XhtmlNode sp = renderStatus(ec, tr.td("structure").colspan(2).spanClss("self-link-parent")); 3859 sp.span("color: grey", null).tx("--"); 3860 sp.b().tx(". "+title); 3861 3862 generateElementInner(status, t, sd, ec, mode, null, null, null, true, defPath, anchorPrefix, new ArrayList<ElementDefinition>(), res); 3863 if (ec.hasSlicing()) { 3864 generateSlicing(t, sd, ec, ec.getSlicing(), null, mode, true); 3865 } 3866 } 3867 } 3868 3869 public ElementDefinition getElementById(String url, String id, StructureDefinition srcSd) { 3870 Map<String, ElementDefinition> sdCache = sdMapCache.get(url); 3871 3872 if (sdCache == null) { 3873 StructureDefinition sd = (StructureDefinition) context.getProfileUtilities().findProfile(url, srcSd); 3874 if (sd == null) { 3875 if (url.equals("http://hl7.org/fhir/StructureDefinition/Base")) { 3876 sd = (StructureDefinition) context.getProfileUtilities().findProfile("http://hl7.org/fhir/StructureDefinition/Element", null); 3877 } 3878 if (sd == null) { 3879 throw new FHIRException(context.formatPhrase(RenderingContext.STRUC_DEF_FHIR_EXCEP, url)+" "); 3880 } 3881 } 3882 sdCache = new HashMap<String, ElementDefinition>(); 3883 sdMapCache.put(url, sdCache); 3884 String webroot = sd.getUserString(UserDataNames.render_webroot); 3885 for (ElementDefinition e : sd.getSnapshot().getElement()) { 3886 context.getProfileUtilities().updateURLs(sd.getUrl(), webroot, e, false); 3887 sdCache.put(e.getId(), e); 3888 } 3889 } 3890 return sdCache.get(id); 3891 } 3892 3893 3894 // Returns the ElementDefinition for the 'parent' of the current element 3895 private ElementDefinition getBaseElement(ElementDefinition e, String url, StructureDefinition sd) { 3896 if (e.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER)) { 3897 return getElementById(url, e.getUserString(UserDataNames.SNAPSHOT_DERIVATION_POINTER), sd); 3898 } 3899 return null; 3900 } 3901 3902 // Returns the ElementDefinition for the 'root' ancestor of the current element 3903 private ElementDefinition getRootElement(ElementDefinition e, StructureDefinition sd) { 3904 if (!e.hasBase()) 3905 return null; 3906 String basePath = e.getBase().getPath(); 3907 String url = "http://hl7.org/fhir/StructureDefinition/" + (basePath.contains(".") ? basePath.substring(0, basePath.indexOf(".")) : basePath); 3908 try { 3909 return getElementById(url, basePath, sd); 3910 } catch (FHIRException except) { 3911 // Likely a logical model, so this is ok 3912 return null; 3913 } 3914 } 3915 private void checkInScope(List<ElementDefinition> stack, List<ElementDefinition> excluded) { 3916 if (stack.size() > 2) { 3917 ElementDefinition parent = stack.get(stack.size()-2); 3918 ElementDefinition focus = stack.get(stack.size()-1); 3919 3920 if (excluded.contains(parent) || "0".equals(parent.getMax())) { 3921 excluded.add(focus); 3922 } 3923 } 3924 } 3925 3926 private void generateAnchors(List<ElementDefinition> stack, Map<String, ElementDefinition> allAnchors) { 3927 List<String> list = new ArrayList<>(); 3928 list.add(stack.get(0).getId()); // initialise 3929 for (int i = 1; i < stack.size(); i++) { 3930 ElementDefinition ed = stack.get(i); 3931 List<String> aliases = new ArrayList<>(); 3932 String name = tail(ed.getPath()); 3933 if (name.endsWith("[x]")) { 3934 aliases.add(name); 3935 Set<String> tl = new HashSet<String>(); // guard against duplicate type names - can happn in some versions 3936 for (TypeRefComponent tr : ed.getType()) { 3937 String tc = tr.getWorkingCode(); 3938 if (!tl.contains(tc)) { 3939 aliases.add(name.replace("[x]", Utilities.capitalize(tc))); 3940 aliases.add(name+":"+name.replace("[x]", Utilities.capitalize(tc))); 3941 aliases.add(name.replace("[x]", Utilities.capitalize(tc))+":"+name.replace("[x]", Utilities.capitalize(tc))); 3942 tl.add(tc); 3943 } 3944 } 3945 } else if (ed.hasSliceName()) { 3946 aliases.add(name+":"+ed.getSliceName()); 3947 // names.add(name); no good generating this? 3948 } else { 3949 aliases.add(name); 3950 } 3951 List<String> generated = new ArrayList<>(); 3952 for (String l : list) { 3953 for (String a : aliases) { 3954 generated.add(l+"."+a); 3955 } 3956 } 3957 list.clear(); 3958 list.addAll(generated); 3959 } 3960 ElementDefinition ed = stack.get(stack.size()-1); 3961 // now we have all the possible names, but some of them might be inappropriate if we've 3962 // already generated a type slicer. On the other hand, if we've already done that, we're 3963 // going to steal any type specific ones off it. 3964 List<String> removed = new ArrayList<>(); 3965 for (String s : list) { 3966 if (!allAnchors.containsKey(s)) { 3967 allAnchors.put(s, ed); 3968 } else if (s.endsWith("[x]")) { 3969 // that belongs on the earlier element 3970 removed.add(s); 3971 } else { 3972 // we delete it from the other 3973 @SuppressWarnings("unchecked") 3974 List<String> other = (List<String>) allAnchors.get(s).getUserData(UserDataNames.render_dict_generator_anchors); 3975 other.remove(s); 3976 allAnchors.put(s, ed); 3977 } 3978 } 3979 list.removeAll(removed); 3980 ed.setUserData(UserDataNames.render_dict_generator_anchors, list); 3981 } 3982 3983 private void addToStack(List<ElementDefinition> stack, ElementDefinition ec) { 3984 while (!stack.isEmpty() && !isParent(stack.get(stack.size()-1), ec)) { 3985 stack.remove(stack.size()-1); 3986 } 3987 stack.add(ec); 3988 } 3989 3990 private boolean isParent(ElementDefinition ed, ElementDefinition ec) { 3991 return ec.getPath().startsWith(ed.getPath()+"."); 3992 } 3993 3994 private List<String> makeAnchors(ElementDefinition ed, String anchorPrefix) { 3995 List<String> list = (List<String>) ed.getUserData(UserDataNames.render_dict_generator_anchors); 3996 List<String> res = new ArrayList<>(); 3997 res.add(anchorPrefix + ed.getId()); 3998 for (String s : list) { 3999 if (!s.equals(ed.getId())) { 4000 res.add(anchorPrefix + s); 4001 } 4002 } 4003 return res; 4004 } 4005 4006 4007 4008 private void link(XhtmlNode x, String id, String anchorPrefix) { 4009 var ah = x.ah(context.prefixLocalHref("#" + anchorPrefix + id)); 4010 ah.attribute("title", "link to here"); 4011 ah.attribute("class", "self-link"); 4012 var svg = ah.svg(); 4013 svg.attribute("viewBox", "0 0 1792 1792"); 4014 svg.attribute("width", "16"); 4015 svg.attribute("height", "16"); 4016 svg.attribute("class", "self-link"); 4017 svg.path("M1520 1216q0-40-28-68l-208-208q-28-28-68-28-42 0-72 32 3 3 19 18.5t21.5 21.5 15 19 13 25.5 3.5 27.5q0 40-28 68t-68 28q-15 0-27.5-3.5t-25.5-13-19-15-21.5-21.5-18.5-19q-33 31-33 73 0 40 28 68l206 207q27 27 68 27 40 0 68-26l147-146q28-28 28-67zm-703-705q0-40-28-68l-206-207q-28-28-68-28-39 0-68 27l-147 146q-28 28-28 67 0 40 28 68l208 208q27 27 68 27 42 0 72-31-3-3-19-18.5t-21.5-21.5-15-19-13-25.5-3.5-27.5q0-40 28-68t68-28q15 0 27.5 3.5t25.5 13 19 15 21.5 21.5 18.5 19q33-31 33-73zm895 705q0 120-85 203l-147 146q-83 83-203 83-121 0-204-85l-206-207q-83-83-83-203 0-123 88-209l-88-88q-86 88-208 88-120 0-204-84l-208-208q-84-84-84-204t85-203l147-146q83-83 203-83 121 0 204 85l206 207q83 83 83 203 0 123-88 209l88 88q86-88 208-88 120 0 204 84l208 208q84 84 84 204z"); 4018 } 4019 4020 private boolean isProfiledExtension(ElementDefinition ec) { 4021 return ec.getType().size() == 1 && "Extension".equals(ec.getType().get(0).getWorkingCode()) && ec.getType().get(0).hasProfile(); 4022 } 4023 4024 private ElementDefinition getExtensionValueDefinition(StructureDefinition extDefn) { 4025 for (ElementDefinition ed : extDefn.getSnapshot().getElement()) { 4026 if (ed.getPath().startsWith("Extension.value")) 4027 return ed; 4028 } 4029 return null; 4030 } 4031 4032 public XhtmlNode compareMarkdown(String location, PrimitiveType md, PrimitiveType compare, int mode) throws FHIRException, IOException { 4033 XhtmlNode ndiv = new XhtmlNode(NodeType.Element, "div"); 4034 if (compare == null || mode == GEN_MODE_DIFF) { 4035 if (md.hasValue()) { 4036 String xhtml = hostMd.processMarkdown(location, md); 4037 if (Utilities.noString(xhtml)) { 4038 return null; 4039 } 4040 try { 4041 renderStatusDiv(md, ndiv).addChildren(fixFontSizes(new XhtmlParser().parseMDFragment(xhtml), 11)); 4042 } catch (Exception e) { 4043 ndiv.span("color: maroon").tx(e.getLocalizedMessage()); 4044 e.printStackTrace(); 4045 } 4046 return ndiv; 4047 } else { 4048 return null; 4049 } 4050 } else if (areEqual(compare, md)) { 4051 if (md.hasValue()) { 4052 String xhtml = hostMd.processMarkdown(location, md); 4053 List<XhtmlNode> nodes = new XhtmlParser().parseMDFragment(xhtml); 4054 for (XhtmlNode n : nodes) { 4055 if (n.getNodeType() == NodeType.Element) { 4056 n.style(unchangedStyle()); 4057 } 4058 } 4059 ndiv.addChildren(nodes); 4060 return ndiv; 4061 } else { 4062 return null; 4063 } 4064 } else { 4065 if (md.hasValue()) { 4066 String xhtml = hostMd.processMarkdown(location, md); 4067 List<XhtmlNode> div = new XhtmlParser().parseMDFragment(xhtml); 4068 ndiv.addChildren(div); 4069 } 4070 if (compare.hasValue()) { 4071 String xhtml = "<div>"+hostMd.processMarkdown(location, compare)+"</div>"; 4072 List<XhtmlNode> div = new XhtmlParser().parseMDFragment(xhtml); 4073 for (XhtmlNode n : div) { 4074 if (n.getNodeType() == NodeType.Element) { 4075 n.style(removedStyle()); 4076 } 4077 } 4078 ndiv.br(); 4079 ndiv.addChildren(div); 4080 } 4081 return ndiv; 4082 } 4083 } 4084 4085 private List<XhtmlNode> fixFontSizes(List<XhtmlNode> nodes, int size) { 4086 for (XhtmlNode x : nodes) { 4087 if (Utilities.existsInList(x.getName(), "p", "li") && !x.hasAttribute("style")) { 4088 x.style("font-size: "+size+"px"); 4089 } 4090 if (x.hasChildren()) { 4091 fixFontSizes(x.getChildNodes(), size); 4092 } 4093 } 4094 return nodes; 4095 } 4096 4097 private boolean areEqual(PrimitiveType compare, PrimitiveType md) { 4098 if (compare == null && md == null) { 4099 return true; 4100 } else if (compare != null && md != null) { 4101 String one = compare.getValueAsString(); 4102 String two = md.getValueAsString(); 4103 if (one == null && two == null) { 4104 return true; 4105 } else if (one != null && one.equals(two)) { 4106 return true; 4107 } 4108 } 4109 return false; 4110 } 4111 4112 public XhtmlNode compareString(String newStr, Base source, String nLink, String name, Base parent, String oldStr, String oLink, int mode, boolean externalN, boolean externalO) { 4113 return compareString(newStr, source, nLink, name, parent, oldStr, oLink, mode, externalN, externalO, false); 4114 } 4115 4116 public XhtmlNode compareString(String newStr, Base source, String nLink, String name, Base parent, String oldStr, String oLink, int mode, boolean externalN, boolean externalO, boolean code) { 4117 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 4118 if (mode != GEN_MODE_KEY) { 4119 if (newStr != null) { 4120 renderStatus(source, x).ah(context.prefixLocalHref(nLink)).txOrCode(code, newStr).iff(externalN).txN(" ").img("external.png", null); 4121 } else if (VersionComparisonAnnotation.hasDeleted(parent, name)) { 4122 PrimitiveType p = (PrimitiveType) VersionComparisonAnnotation.getDeletedItem(parent, name); 4123 renderStatus(p, x).txOrCode(code, p.primitiveValue()); 4124 } else { 4125 return null; 4126 } 4127 } else if (oldStr==null || oldStr.isEmpty()) { 4128 if (newStr==null || newStr.isEmpty()) { 4129 return null; 4130 } else { 4131 renderStatus(source, x).ah(context.prefixLocalHref(nLink)).txOrCode(code, newStr).iff(externalN).txN(" ").img("external.png", null); 4132 } 4133 } else if (oldStr!=null && !oldStr.isEmpty() && (newStr==null || newStr.isEmpty())) { 4134 if (mode == GEN_MODE_DIFF) { 4135 return null; 4136 } else { 4137 removed(x).ah(context.prefixLocalHref(oLink)).txOrCode(code, oldStr).iff(externalO).txN(" ").img("external.png", null); 4138 } 4139 } else if (oldStr.equals(newStr)) { 4140 if (mode==GEN_MODE_DIFF) { 4141 return null; 4142 } else { 4143 unchanged(x).ah(context.prefixLocalHref(nLink)).txOrCode(code, newStr).iff(externalN).txN(" ").img("external.png", null); 4144 } 4145 } else if (newStr.startsWith(oldStr)) { 4146 unchanged(x).ah(context.prefixLocalHref(oLink)).txOrCode(code, oldStr).iff(externalO).txN(" ").img("external.png", null); 4147 renderStatus(source, x).ah(context.prefixLocalHref(nLink)).txN(newStr.substring(oldStr.length())).iff(externalN).txN(" ").img("external.png", null); 4148 } else { 4149 // TODO: improve comparision in this fall-through case, by looking for matches in sub-paragraphs? 4150 renderStatus(source, x).ah(context.prefixLocalHref(nLink)).txOrCode(code, newStr).iff(externalN).txN(" ").img("external.png", null); 4151 removed(x).ah(context.prefixLocalHref(oLink)).txOrCode(code, oldStr).iff(externalO).txN(" ").img("external.png", null); 4152 } 4153 return x; 4154 } 4155 4156 public boolean compareString(XhtmlNode x, String newStr, Base source, String nLink, String name, Base parent, String oldStr, String oLink, int mode, boolean externalN, boolean externalO) { 4157 XhtmlNode x1 = compareString(newStr, source, nLink, name, parent, oldStr, oLink, mode, externalN, externalO); 4158 if (x1 == null) { 4159 return false; 4160 } else { 4161 x.addChildNodes(x1.getChildNodes()); 4162 return true; 4163 } 4164 } 4165 4166 public XhtmlNode unchanged(XhtmlNode x) { 4167 return x.span(unchangedStyle()); 4168 } 4169 4170 private String unchangedStyle() { 4171 return "color:DarkGray"; 4172 } 4173 4174 public XhtmlNode removed(XhtmlNode x) { 4175 return x.span(removedStyle()); 4176 } 4177 4178 private String removedStyle() { 4179 return "color:DarkGray;text-decoration:line-through"; 4180 } 4181 4182 private void generateElementInner(RenderingStatus status, XhtmlNode tbl, StructureDefinition sd, ElementDefinition d, int mode, ElementDefinition value, ElementDefinition compare, ElementDefinition compareValue, boolean strikethrough, String defPath, String anchorPrefix, List<ElementDefinition> inScopeElements, ResourceWrapper res) throws FHIRException, IOException { 4183 boolean root = !d.getPath().contains("."); 4184 boolean slicedExtension = d.hasSliceName() && (d.getPath().endsWith(".extension") || d.getPath().endsWith(".modifierExtension")); 4185// int slicedExtensionMode = (mode == GEN_MODE_KEY) && slicedExtension ? GEN_MODE_SNAP : mode; // see ProfileUtilities.checkExtensionDoco / Task 3970 4186 if (d.hasSliceName()) { 4187 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_SLICE_NAME), "profiling.html#slicing", strikethrough, compareString(d.getSliceName(), d.getSliceNameElement(), null, (compare != null ? compare.getSliceName() : null), d, null, "sliceName", mode, false, false)); 4188 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_CONSTRAINING), "profiling.html#slicing", strikethrough, compareString(encodeValue(d.getSliceIsConstrainingElement(), null), d.getSliceIsConstrainingElement(), null, (compare != null ? encodeValue(compare.getSliceIsConstrainingElement(), null) : null), d, null, "sliceName", mode, false, false)); 4189 } 4190 4191 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_DEFINITION), null, strikethrough, compareMarkdown(sd.getName(), d.getDefinitionElement(), (compare==null) || slicedExtension ? null : compare.getDefinitionElement(), mode)); 4192 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_SHORT), null, strikethrough, compareString(d.hasShort() ? d.getShort() : null, d.getShortElement(), null, "short", d, compare!= null && compare.hasShortElement() ? compare.getShort() : null, null, mode, false, false)); 4193 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_COMMENTS), null, strikethrough, compareMarkdown(sd.getName(), d.getCommentElement(), (compare==null) || slicedExtension ? null : compare.getCommentElement(), mode)); 4194 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_NOTE), null, strikethrough, businessIdWarning(sd.getName(), tail(d.getPath()))); 4195 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_CONTROL), "conformance-rules.html#conformance", strikethrough, describeCardinality(d, compare, mode)); 4196 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_BINDING), "terminologies.html", strikethrough, describeBinding(sd, d, d.getPath(), compare, mode)); 4197 if (d.hasContentReference()) { 4198 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_TYPE), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_SEE) + d.getContentReference().substring(1)); 4199 } else { 4200 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_TYPE), "datatypes.html", strikethrough, describeTypes(d.getType(), false, d, compare, mode, value, compareValue, sd)); 4201 } 4202 if (root && sd.hasExtension(ExtensionDefinitions.EXT_TYPE_PARAMETER)) { 4203 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_PARAMETER), "http://hl7.org/fhir/tools/StructureDefinition-type-parameter.html", strikethrough, renderTypeParameter(sd.getExtensionByUrl(ExtensionDefinitions.EXT_TYPE_PARAMETER))); 4204 } 4205 if (d.hasExtension(ExtensionDefinitions.EXT_DEF_TYPE)) { 4206 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_DEFAULT_TYPE), "datatypes.html", strikethrough, ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_DEF_TYPE)); 4207 } 4208 if (d.hasExtension(ExtensionDefinitions.EXT_TYPE_SPEC)) { 4209 tableRow(tbl, Utilities.pluralize(context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_SPEC), d.getExtensionsByUrl(ExtensionDefinitions.EXT_TYPE_SPEC).size()), "datatypes.html", strikethrough, formatTypeSpecifiers(d)); 4210 } 4211 if (d.getPath().endsWith("[x]") && !d.prohibited()) { 4212 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_NOTE_X), null, strikethrough).ahWithText(context.formatPhrase(RenderingContext.STRUC_DEF_SEE) 4213 , spec("formats.html#choice"), null, context.formatPhrase(RenderingContext.STRUC_DEF_CHOICE_DATA_TYPE), context.formatPhrase(RenderingContext.STRUC_DEF_FURTHER_INFO)); 4214 } 4215 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_MODIFIER), "conformance-rules.html#ismodifier", strikethrough, presentModifier(d, mode, compare)); 4216 if (d.getMustHaveValue()) { 4217 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_PRIMITIVE), "elementdefinition.html#primitives", strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_PRIM_TYPE_VALUE)); 4218 } else if (d.hasValueAlternatives()) { 4219 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_PRIMITIVE), "elementdefinition.html#primitives", strikethrough, renderCanonicalList(context.formatPhrase(RenderingContext.STRUC_DEF_PRIM_TYPE_PRESENT), d.getValueAlternatives())); 4220 } else if (hasPrimitiveTypes(d)) { 4221 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_PRIMITIVE), "elementdefinition.html#primitives", strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_PRIM_ELE)); 4222 } 4223 if (ExtensionUtilities.hasAllowedUnits(d)) { 4224 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_ALLOWED), "http://hl7.org/fhir/extensions/StructureDefinition-elementdefinition-allowedUnits.html", strikethrough, describeAllowedUnits(d)); 4225 } 4226 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_MUST_SUPPORT), "conformance-rules.html#mustSupport", strikethrough, displayBoolean(d.getMustSupport(), d.getMustSupportElement(), "mustSupport", d, compare==null ? null : compare.getMustSupportElement(), mode)); 4227 if (d.getMustSupport()) { 4228 if (hasMustSupportTypes(d.getType())) { 4229 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_MUST_SUPPORT_TYPES), "datatypes.html", strikethrough, describeTypes(d.getType(), true, d, compare, mode, null, null, sd)); 4230 } else if (hasChoices(d.getType())) { 4231 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_MUST_SUPPORT_TYPES), "datatypes.html", strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_NO_MUST_SUPPORT)); 4232 } 4233 } 4234 if (root && sd.getKind() == StructureDefinitionKind.LOGICAL) { 4235 Extension lt = ExtensionUtilities.getExtension(sd, ExtensionDefinitions.EXT_LOGICAL_TARGET); 4236 if (lt == null || !lt.hasValue()) { 4237 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_LOGICAL), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_NOT_MARK)); 4238 } else if (lt.getValue().hasExtension(ExtensionDefinitions.EXT_DAR)) { 4239 } else if (lt.getValueBooleanType().hasValue()) { 4240 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_LOGICAL), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_NOT_MARK)); 4241 } else if (lt.getValueBooleanType().booleanValue()) { 4242 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_LOGICAL), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_CAN_TARGET)); 4243 } else { 4244 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_LOGICAL), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_CANNOT_TARGET)); 4245 } 4246 4247 Extension lc = ExtensionUtilities.getExtension(sd, ExtensionDefinitions.EXT_LOGICAL_CONTAINER); 4248 if (lc != null && lc.hasValueUriType()) { 4249 String uri = lc.getValue().primitiveValue(); 4250 StructureDefinition lct = context.getContext().fetchTypeDefinition(uri); 4251 if (lct != null) { 4252 tableRowLink(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_LOGICAL_CONT), null, strikethrough, lct.present(), lct.getWebPath()); 4253 } else { 4254 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_LOGICAL_CONT), null, strikethrough, uri); 4255 } 4256 } 4257 4258 String ps = ExtensionUtilities.readStringExtension(sd, ExtensionDefinitions.EXT_PROFILE_STYLE); 4259 if (ps != null) { 4260 if ("cda".equals(ps)) { 4261 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_VALID), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_TEMPLATEID)); 4262 } else { 4263 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_VALID), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_UNKNOWN_APPROACH, ps)+" "); 4264 } 4265 } 4266 } 4267 4268 if (root && sd.hasExtension(ExtensionDefinitions.EXT_SD_IMPOSE_PROFILE)) { 4269 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_IMPOSE_PROFILE), "http://hl7.org/fhir/extensions/StructureDefinition-structuredefinition-imposeProfile.html", strikethrough, 4270 renderCanonicalListExt(context.formatPhrase(RenderingContext.STRUC_DEF_PROF_REQ)+" ", sd.getExtensionsByUrl(ExtensionDefinitions.EXT_SD_IMPOSE_PROFILE))); 4271 } 4272 if (root && sd.hasExtension(ExtensionDefinitions.EXT_SD_COMPLIES_WITH_PROFILE)) { 4273 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_COMP_PROF), "http://hl7.org/fhir/extensions/StructureDefinition-structuredefinition-compliesWithProfile.html", strikethrough, 4274 renderCanonicalListExt(context.formatPhrase(RenderingContext.STRUC_DEF_PROF_COMP)+" ", sd.getExtensionsByUrl(ExtensionDefinitions.EXT_SD_COMPLIES_WITH_PROFILE))); 4275 } 4276 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_OBLIG), null, strikethrough, describeObligations(status, d, root, sd, defPath, anchorPrefix, inScopeElements, res)); 4277 4278 if (d.hasExtension(ExtensionDefinitions.EXT_EXTENSION_STYLE_NEW, ExtensionDefinitions.EXT_EXTENSION_STYLE_DEPRECATED)) { 4279 String es = d.getExtensionString(ExtensionDefinitions.EXT_EXTENSION_STYLE_NEW, ExtensionDefinitions.EXT_EXTENSION_STYLE_DEPRECATED); 4280 if ("named-elements".equals(es)) { 4281 if (context.hasLink(KnownLinkType.JSON_NAMES)) { 4282 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_EXT_STYLE), context.getLink(KnownLinkType.JSON_NAMES, true), strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_EXT_JSON)); 4283 } else { 4284 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_EXT_STYLE), ExtensionDefinitions.WEB_EXTENSION_STYLE, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_EXT_JSON)); 4285 } 4286 } 4287 } 4288 4289 if (!d.getPath().contains(".") && ExtensionUtilities.hasExtension(sd, ExtensionDefinitions.EXT_BINDING_STYLE)) { 4290 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_BINDING_STYLE), ExtensionDefinitions.WEB_BINDING_STYLE, strikethrough, 4291 context.formatPhrase(RenderingContext.STRUC_DEF_TYPE_BOUND, ExtensionUtilities.readStringExtension(sd, ExtensionDefinitions.EXT_BINDING_STYLE)+" binding style")+" "); 4292 } 4293 4294 if (d.hasExtension(ExtensionDefinitions.EXT_DATE_FORMAT)) { 4295 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_DATE_FORM), null, strikethrough, ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_DATE_FORMAT)); 4296 } 4297 String ide = ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_ID_EXPECTATION); 4298 if (ide != null) { 4299 if (ide.equals("optional")) { 4300 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_ID_EXPECT), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_ID_IS)); 4301 } else if (ide.equals("required")) { 4302 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_ID_EXPECT), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_ID_MAY)); 4303 } else if (ide.equals("required")) { 4304 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_ID_EXPECT), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_ID_NOT_ALLOW)); 4305 } 4306 } 4307 4308 if (d.hasExtension(ExtensionDefinitions.EXT_ID_CHOICE_GROUP)) { 4309 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_CHOICE_GRP), null, strikethrough, context.formatPhrase(RenderingContext.STRUC_DEF_REPEAT)); 4310 } 4311 4312 // tooling extensions for formats 4313 if (ExtensionUtilities.hasAnyOfExtensions(d, ExtensionDefinitions.EXT_JSON_EMPTY, ExtensionDefinitions.EXT_JSON_PROP_KEY, ExtensionDefinitions.EXT_JSON_NULLABLE, 4314 ExtensionDefinitions.EXT_JSON_NAME, ExtensionDefinitions.EXT_JSON_NAME_DEPRECATED, ExtensionDefinitions.EXT_JSON_PRIMITIVE_CHOICE)) { 4315 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_JSON_FORM), null, strikethrough, describeJson(d)); 4316 } 4317 if (d.hasExtension(ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED) || sd.hasExtension(ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED) || 4318 d.hasExtension(ExtensionDefinitions.EXT_XML_NAME, ExtensionDefinitions.EXT_XML_NAME_DEPRECATED) || sd.hasExtension(ExtensionDefinitions.EXT_XML_NAME, ExtensionDefinitions.EXT_XML_NAME_DEPRECATED) || 4319 d.hasRepresentation()) { 4320 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_XML_FORM), null, strikethrough, describeXml(sd, d, root)); 4321 } 4322 4323 if (d.hasExtension(ExtensionDefinitions.EXT_IMPLIED_PREFIX)) { 4324 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_STRING_FORM), null, strikethrough).codeWithText(context.formatPhrase(RenderingContext.STRUC_DEF_ELE_READ)+" ", ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_IMPLIED_PREFIX), context.formatPhrase(RenderingContext.STRUC_DEF_PREFIXED)); 4325 } 4326 4327 if (d.hasExtension(ExtensionDefinitions.EXT_STANDARDS_STATUS)) { 4328 StandardsStatus ss = StandardsStatus.fromCode(d.getExtensionString(ExtensionDefinitions.EXT_STANDARDS_STATUS)); 4329 // gc.addStyledText("Standards Status = "+ss.toDisplay(), ss.getAbbrev(), "black", ss.getColor(), baseSpecUrl()+, true); 4330 StructureDefinition sdb = context.getProfileUtilities().findProfile(sd.getBaseDefinition(), sd); 4331 if (sdb != null) { 4332 StandardsStatus base = determineStandardsStatus(sdb, (ElementDefinition) d.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER)); 4333 if (base != null) { 4334 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_STAND_STAT), "versions.html#std-process", strikethrough, ss.toDisplay()+" (from "+base.toDisplay()+")"); 4335 } else { 4336 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_STAND_STAT), "versions.html#std-process", strikethrough, ss.toDisplay()); 4337 } 4338 } else { 4339 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_STAND_STAT), "versions.html#std-process", strikethrough, ss.toDisplay()); 4340 } 4341 } 4342 if (mode != GEN_MODE_DIFF && d.hasIsSummary()) { 4343 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_SUMM), "search.html#summary", strikethrough, Boolean.toString(d.getIsSummary())); 4344 } 4345 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_REQUIREMENTS), null, strikethrough, compareMarkdown(sd.getName(), d.getRequirementsElement(), (compare==null) || slicedExtension ? null : compare.getRequirementsElement(), mode)); 4346 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_LABEL), null, strikethrough, compareString(d.getLabel(), d.getLabelElement(), null, "label", d, (compare != null ? compare.getLabel() : null), null, mode, false, false)); 4347 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_ALT_NAME), null, strikethrough, compareSimpleTypeLists(d.getAlias(), ((compare==null) || slicedExtension ? null : compare.getAlias()), mode)); 4348 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_DEF_CODES), null, strikethrough, compareDataTypeLists(d.getCode(), ((compare==null) || slicedExtension ? null : compare.getCode()), mode)); 4349 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_MIN_VALUE), null, strikethrough, compareString(d.hasMinValue() ? encodeValue(d.getMinValue(), null) : null, d.getMinValue(), null, "minValue", d, compare!= null && compare.hasMinValue() ? encodeValue(compare.getMinValue(), null) : null, null, mode, false, false)); 4350 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_MAX_VALUE), null, strikethrough, compareString(d.hasMaxValue() ? encodeValue(d.getMaxValue(), null) : null, d.getMaxValue(), null, "maxValue", d, compare!= null && compare.hasMaxValue() ? encodeValue(compare.getMaxValue(), null) : null, null, mode, false, false)); 4351 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_MAX_LENGTH), null, strikethrough, compareString(d.hasMaxLength() ? toStr(d.getMaxLength()) : null, d.getMaxLengthElement(), null, "maxLength", d, compare!= null && compare.hasMaxLengthElement() ? toStr(compare.getMaxLength()) : null, null, mode, false, false)); 4352 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_MIN_LENGTH), null, strikethrough, ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_MIN_LENGTH)); 4353 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_VALUE_REQ), null, strikethrough, compareString(encodeValue(d.getMustHaveValueElement(), null), d.getMustHaveValueElement(), null, (compare != null ? encodeValue(compare.getMustHaveValueElement(), null) : null), d, null, "mustHaveValueElement", mode, false, false)); 4354 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_VALUE_ALT), null, strikethrough, compareSimpleTypeLists(d.getValueAlternatives(), ((compare==null) || slicedExtension ? null : compare.getValueAlternatives()), mode)); 4355 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_DEFAULT_VALUE), null, strikethrough, encodeValue(d.getDefaultValue(), "defaultValue", d, compare==null ? null : compare.getDefaultValue(), mode, d.getName())); 4356 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_MEAN_MISS), null, strikethrough, d.getMeaningWhenMissing()); 4357 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_FIXED), null, strikethrough, encodeValue(d.getFixed(), "fixed", d, compare==null ? null : compare.getFixed(), mode, d.getName())); 4358 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_PATT_VALUE), null, strikethrough, encodeValue(d.getPattern(), "pattern", d, compare==null ? null : compare.getPattern(), mode, d.getName())); 4359 tableRow(tbl, context.formatPhrase(RenderingContext.GENERAL_EXAMPLE), null, strikethrough, encodeValues(d.getExample())); 4360 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_INVAR), null, strikethrough, invariants(d.getConstraint(), compare==null ? null : compare.getConstraint(), d, mode)); 4361 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_LOINC), null, strikethrough, getMapping(sd, d, LOINC_MAPPING, compare, mode)); 4362 tableRow(tbl, context.formatPhrase(RenderingContext.STRUC_DEF_SNOMED), null, strikethrough, getMapping(sd, d, SNOMED_MAPPING, compare, mode)); 4363 tbl.tx("\r\n"); 4364 } 4365 4366 private XhtmlNode renderTypeParameter(Extension ext) { 4367 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 4368 x.tx(ext.getExtensionString("name")); 4369 x.tx(" : "); 4370 String t = ext.getExtensionString("type"); 4371 StructureDefinition sd = context.getContext().fetchTypeDefinition(t); 4372 if (sd == null) { 4373 x.code().tx(t); 4374 } else { 4375 x.ah(context.prefixLocalHref(sd.getWebPath()), t).tx(sd.present()); 4376 } 4377 return x; 4378 } 4379 4380 private XhtmlNode presentModifier(ElementDefinition d, int mode, ElementDefinition compare) throws FHIRException, IOException { 4381 XhtmlNode x1 = compareString(encodeValue(d.getIsModifierElement(), null), d.getIsModifierElement(), null, "isModifier", d, compare == null ? null : encodeValue(compare.getIsModifierElement(), null), null, mode, false, false); 4382 if (x1 != null) { 4383 XhtmlNode x2 = compareString(encodeValue(d.getIsModifierReasonElement(), null), d.getIsModifierReasonElement(), null, "isModifierReason", d, compare == null ? null : encodeValue(compare.getIsModifierReasonElement(), null), null, mode, false, false); 4384 if (x2 != null) { 4385 x1.tx(" "+(context.formatPhrase(RenderingContext.STRUC_DEF_BECAUSE)+" ")); 4386 x1.copyAllContent(x2); 4387 } 4388 } 4389 return x1; 4390 } 4391 4392 private String spec(String name) { 4393 return Utilities.pathURL(VersionUtilities.getSpecUrl(context.getWorker().getVersion()) , name); 4394 } 4395 4396 private XhtmlNode describeXml(StructureDefinition profile, ElementDefinition d, boolean root) { 4397 XhtmlNode ret = new XhtmlNode(NodeType.Element, "div"); 4398 for (PropertyRepresentation pr : PropertyRepresentation.values()) { 4399 if (d.hasRepresentation(pr)) { 4400 switch (pr) { 4401 case CDATEXT: 4402 ret.tx(context.formatPhrase(RenderingContext.STRUC_DEF_CDA)); 4403 break; 4404 case TYPEATTR: 4405 ret.codeWithText((context.formatPhrase(RenderingContext.STRUC_DEF_XSI)+" "), "xsi:type", "attribute."); 4406 break; 4407 case XHTML: 4408 ret.tx(context.formatPhrase(RenderingContext.STRUC_DEF_XHTML)); 4409 break; 4410 case XMLATTR: 4411 ret.tx(context.formatPhrase(RenderingContext.STRUC_DEF_XML_ATTRIBUTE)); 4412 break; 4413 case XMLTEXT: 4414 ret.tx(context.formatPhrase(RenderingContext.STRUC_DEF_UNADORNED)); 4415 break; 4416 default: 4417 } 4418 } 4419 } 4420 String name = ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED); 4421 if (name == null && root) { 4422 name = ExtensionUtilities.readStringExtension(profile, ExtensionDefinitions.EXT_XML_NAMESPACE, ExtensionDefinitions.EXT_XML_NAMESPACE_DEPRECATED); 4423 } 4424 if (name != null) { 4425 ret.codeWithText(context.formatPhrase(RenderingContext.STRUC_DEF_NAMESPACE)+" ", name, "."); 4426 } 4427 name = ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_XML_NAME, ExtensionDefinitions.EXT_XML_NAME_DEPRECATED); 4428 if (name != null) { 4429 ret.codeWithText(context.formatPhrase(RenderingContext.STRUC_DEF_XML_ACTUAL), name, "."); 4430 } 4431 return ret; 4432 } 4433 4434 private XhtmlNode describeJson(ElementDefinition d) { 4435 XhtmlNode ret = new XhtmlNode(NodeType.Element, "div"); 4436 var ul = ret.ul(); 4437 boolean list = ExtensionUtilities.countExtensions(d, ExtensionDefinitions.EXT_JSON_EMPTY, ExtensionDefinitions.EXT_JSON_PROP_KEY, ExtensionDefinitions.EXT_JSON_NULLABLE, ExtensionDefinitions.EXT_JSON_NAME, ExtensionDefinitions.EXT_JSON_NAME_DEPRECATED) > 1; 4438 4439 String code = ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_JSON_EMPTY); 4440 if (code != null) { 4441 switch (code) { 4442 case "present": 4443 ul.li().tx(context.formatPhrase(RenderingContext.STRUC_DEF_JSON_PRESENT)); 4444 break; 4445 case "absent": 4446 ul.li().tx(context.formatPhrase(RenderingContext.STRUC_DEF_JSON_NOT_PRESENT)); 4447 break; 4448 case "either": 4449 ul.li().tx(context.formatPhrase(RenderingContext.STRUC_DEF_JSON_MAY_PRESENT)); 4450 break; 4451 } 4452 } 4453 String jn = ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_JSON_NAME, ExtensionDefinitions.EXT_JSON_NAME_DEPRECATED); 4454 if (jn != null) { 4455 if (d.getPath().contains(".")) { 4456 ul.li().codeWithText(context.formatPhrase(RenderingContext.STRUC_DEF_JSON_PROPERTY_NAME), jn, null); 4457 } else { 4458 ul.li().codeWithText(context.formatPhrase(RenderingContext.STRUC_DEF_JSON_CAN_NAME), jn, " " + context.formatPhrase(RenderingContext.STRUC_DEF_JSON_EXT)); 4459 } 4460 } 4461 code = ExtensionUtilities.readStringExtension(d, ExtensionDefinitions.EXT_JSON_PROP_KEY); 4462 if (code != null) { 4463 ul.li().codeWithText(context.formatPhrase(RenderingContext.STRUC_DEF_JSON_SINGLE), code, " "+ context.formatPhrase(RenderingContext.STRUC_DEF_JSON_CHILD)); 4464 } 4465 if (ExtensionUtilities.readBoolExtension(d, ExtensionDefinitions.EXT_JSON_NULLABLE)) { 4466 ul.li().tx(context.formatPhrase(RenderingContext.STRUC_DEF_NULL_JSON)); 4467 } 4468 if (ExtensionUtilities.readBoolExtension(d, ExtensionDefinitions.EXT_JSON_PRIMITIVE_CHOICE)) { 4469 ul.li().tx(context.formatPhrase(RenderingContext.STRUC_DEF_INFERRED_JSON)); 4470 } 4471 4472 switch (ul.getChildNodes().size()) { 4473 case 0: return null; 4474 case 1: return ul.getChildNodes().get(0); 4475 default: return ret; 4476 } 4477 } 4478 4479 private XhtmlNode describeObligations(RenderingStatus status, ElementDefinition d, boolean root, StructureDefinition sdx, String defPath, String anchorPrefix, List<ElementDefinition> inScopeElements, ResourceWrapper res) throws IOException { 4480 XhtmlNode ret = new XhtmlNode(NodeType.Element, "div"); 4481 ObligationsRenderer obr = new ObligationsRenderer(corePath, sdx, d.getPath(), context, hostMd, this, true); 4482 obr.seeObligations(d.getExtensionsByUrl(ExtensionDefinitions.EXT_OBLIGATION_CORE, ExtensionDefinitions.EXT_OBLIGATION_TOOLS)); 4483 obr.seeRootObligations(d.getId(), sdx.getExtensionsByUrl(ExtensionDefinitions.EXT_OBLIGATION_CORE, ExtensionDefinitions.EXT_OBLIGATION_TOOLS)); 4484 if (obr.hasObligations() || (root && (sdx.hasExtension(ExtensionDefinitions.EXT_OBLIGATION_PROFILE_FLAG_NEW, ExtensionDefinitions.EXT_OBLIGATION_PROFILE_FLAG_OLD) || sdx.hasExtension(ExtensionDefinitions.EXT_OBLIGATION_INHERITS_NEW, ExtensionDefinitions.EXT_OBLIGATION_INHERITS_OLD)))) { 4485 XhtmlNode ul = ret.ul(); 4486 if (root) { 4487 if (sdx.hasExtension(ExtensionDefinitions.EXT_OBLIGATION_PROFILE_FLAG_NEW, ExtensionDefinitions.EXT_OBLIGATION_PROFILE_FLAG_OLD)) { 4488 ul.li().tx(context.formatPhrase(RenderingContext.STRUC_DEF_OBLIG_ADD)); 4489 } 4490 for (Extension ext : sdx.getExtensionsByUrl(ExtensionDefinitions.EXT_OBLIGATION_INHERITS_NEW, ExtensionDefinitions.EXT_OBLIGATION_INHERITS_OLD)) { 4491 String iu = ext.getValue().primitiveValue(); 4492 XhtmlNode bb = ul.li(); 4493 bb.tx(context.formatPhrase(RenderingContext.STRUC_DEF_OBLIG_FROM)+" "); 4494 StructureDefinition sd = context.getProfileUtilities().findProfile(iu, sdx); 4495 if (sd == null) { 4496 bb.code().tx(iu); 4497 } else if (sd.hasWebPath()) { 4498 bb.ah(context.prefixLocalHref(sd.getWebPath())).tx(sd.present()); 4499 } else { 4500 bb.ah(context.prefixLocalHref(iu)).tx(sd.present()); 4501 } 4502 } 4503 if (ul.isEmpty()) { 4504 ret.remove(ul); 4505 } 4506 } 4507 if (obr.hasObligations()) { 4508 XhtmlNode tbl = ret.table("grid", false).markGenerated(!context.forValidResource()); 4509 obr.renderTable(status, res, tbl.getChildNodes(), true, defPath, anchorPrefix, inScopeElements); 4510 if (tbl.isEmpty()) { 4511 ret.remove(tbl); 4512 } 4513 } 4514 return ret.hasChildren() ? ret : null; 4515 } else { 4516 return null; 4517 } 4518 } 4519 4520 private XhtmlNode describeAllowedUnits(ElementDefinition d) { 4521 XhtmlNode ret = new XhtmlNode(NodeType.Element, "div"); 4522 DataType au = ExtensionUtilities.getAllowedUnits(d); 4523 if (au instanceof CanonicalType) { 4524 String url = ((CanonicalType) au).asStringValue(); 4525 ValueSet vs = context.getContext().findTxResource(ValueSet.class, url); 4526 ret.tx(context.formatPhrase(RenderingContext.GENERAL_VALUESET)+" "); 4527 genCT(ret, url, vs); 4528 return ret; 4529 } else if (au instanceof CodeableConcept) { 4530 CodeableConcept cc = (CodeableConcept) au; 4531 if (cc.getCoding().size() != 1) { 4532 ret.tx(context.formatPhrase(RenderingContext.STRUC_DEF_ONE_OF)); 4533 } 4534 ret.tx(summarise(cc)); 4535 return ret; 4536 } 4537 return null; 4538 } 4539 4540 private void genCT(XhtmlNode x, String url, CanonicalResource cr) { 4541 if (cr == null) { 4542 x.code().tx(url); 4543 } else if (!cr.hasWebPath()) { 4544 x.ah(context.prefixLocalHref(url)).tx(cr.present()); 4545 } else { 4546 x.ah(context.prefixLocalHref(cr.getWebPath())).tx(cr.present()); 4547 } 4548 } 4549 4550 private boolean hasPrimitiveTypes(ElementDefinition d) { 4551 for (TypeRefComponent tr : d.getType()) { 4552 if (context.getContext().isPrimitiveType(tr.getCode())) { 4553 return true; 4554 } 4555 } 4556 return false; 4557 } 4558 4559 4560 private XhtmlNode renderCanonicalListExt(String text, List<Extension> list) { 4561 List<CanonicalType> clist = new ArrayList<>(); 4562 for (Extension ext : list) { 4563 if (ext.hasValueCanonicalType()) { 4564 clist.add(ext.getValueCanonicalType()); 4565 } 4566 } 4567 return renderCanonicalList(text, clist); 4568 } 4569 4570 private XhtmlNode renderCanonicalList(String text, List<CanonicalType> list) { 4571 XhtmlNode ret = new XhtmlNode(NodeType.Element, "div"); 4572 ret.tx(text); 4573 var ul = ret.ul(); 4574 for (CanonicalType ct : list) { 4575 CanonicalResource cr = (CanonicalResource) context.getContext().fetchResource(Resource.class, ct.getValue()); 4576 genCT(ul.li(), ct.getValue(), cr); 4577 } 4578 return ret; 4579 } 4580 4581 private StandardsStatus determineStandardsStatus(StructureDefinition sd, ElementDefinition ed) { 4582 if (ed != null && ed.hasExtension(ExtensionDefinitions.EXT_STANDARDS_STATUS)) { 4583 return StandardsStatus.fromCode(ed.getExtensionString(ExtensionDefinitions.EXT_STANDARDS_STATUS)); 4584 } 4585 while (sd != null) { 4586 if (sd.hasExtension(ExtensionDefinitions.EXT_STANDARDS_STATUS)) { 4587 return ExtensionUtilities.getStandardsStatus(sd); 4588 } 4589 sd = context.getProfileUtilities().findProfile(sd.getBaseDefinition(), sd); 4590 } 4591 return null; 4592 } 4593 4594 private boolean hasChoices(List<TypeRefComponent> types) { 4595 for (TypeRefComponent type : types) { 4596 if (type.getProfile().size() > 1 || type.getTargetProfile().size() > 1) { 4597 return true; 4598 } 4599 } 4600 return types.size() > 1; 4601 } 4602 4603 private String sliceOrderString(ElementDefinitionSlicingComponent slicing) { 4604 if (slicing.getOrdered()) 4605 return context.formatPhrase(RenderingContext.STRUC_DEF_ORDERED); 4606 else 4607 return context.formatPhrase(RenderingContext.STRUC_DEF_UNORDERED); 4608 } 4609 4610 private void generateSlicing(XhtmlNode tbl, StructureDefinition profile, ElementDefinition ed, ElementDefinitionSlicingComponent slicing, ElementDefinition compare, int mode, boolean strikethrough) throws IOException { 4611 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 4612 4613 x.codeWithText(context.formatPhrase(RenderingContext.STRUC_DEF_SET_SLICES)+" ", ed.getPath(), context.formatPhrase(RenderingContext.STRUC_DEF_SET_ARE)); 4614 String newOrdered = sliceOrderString(slicing); 4615 String oldOrdered = (compare==null || !compare.hasSlicing()) ? null : sliceOrderString(compare.getSlicing()); 4616 compareString(x, newOrdered, slicing.getOrderedElement(), null, null, null, oldOrdered, null, mode, false, false); 4617 x.tx(" "+context.formatPhrase(RenderingContext.STRUC_DEF_AND) + " "); 4618 compareString(x, slicing.hasRules() ? slicing.getRules().getDisplay() : null, slicing.getRulesElement(), null, "rules", slicing, compare!=null && compare.hasSlicing() && compare.getSlicing().hasRules() ? compare.getSlicing().getRules().getDisplay() : null, null, mode, false, false); 4619 4620 if (slicing.hasDiscriminator()) { 4621 x.tx(context.formatPhrase(RenderingContext.STRUC_DEF_DESCRIM)); 4622 StatusList<DiscriminatorWithStatus> list = new StatusList<>(); 4623 for (ElementDefinitionSlicingDiscriminatorComponent d : slicing.getDiscriminator()) { 4624 list.add(new DiscriminatorWithStatus(d)); 4625 } 4626 if (compare != null) { 4627 for (ElementDefinitionSlicingDiscriminatorComponent d : slicing.getDiscriminator()) { 4628 list.merge(new DiscriminatorWithStatus(d)); 4629 } 4630 } 4631 var ul = x.ul(); 4632 for (DiscriminatorWithStatus rc : list) { 4633 rc.render(x.li()); 4634 } 4635 } else { 4636 x.tx(context.formatPhrase(RenderingContext.STRUC_DEF_NO_DESCRIM)); 4637 } 4638 tableRow(tbl, "Slicing", "profiling.html#slicing", strikethrough, x); 4639 tbl.tx("\r\n"); 4640 } 4641 4642 private XhtmlNode tableRow(XhtmlNode x, String name, String defRef, boolean strikethrough) throws IOException { 4643 var tr = x.tr(); 4644 if (strikethrough) { 4645 tr.style("text-decoration: line-through"); 4646 } 4647 addFirstCell(name, defRef, tr); 4648 return tr.td(); 4649 } 4650 4651 4652 private void tableRow(XhtmlNode x, String name, String defRef, boolean strikethrough, XhtmlNode possibleTd) throws IOException { 4653 if (possibleTd != null && !possibleTd.isEmpty()) { 4654 var tr = x.tr(); 4655 if (strikethrough) { 4656 tr.style("text-decoration: line-through"); 4657 } 4658 addFirstCell(name, defRef, tr); 4659 tr.td().copyAllContent(possibleTd); 4660 } 4661 } 4662 4663 private void tableRow(XhtmlNode x, String name, String defRef, boolean strikethrough, String text) throws IOException { 4664 if (!Utilities.noString(text)) { 4665 var tr = x.tr(); 4666 if (strikethrough) { 4667 tr.style("text-decoration: line-through"); 4668 } 4669 addFirstCell(name, defRef, tr); 4670 tr.td().tx(text); 4671 } 4672 } 4673 4674 private void tableRowLink(XhtmlNode x, String name, String defRef, boolean strikethrough, String text, String link) throws IOException { 4675 if (!Utilities.noString(text)) { 4676 var tr = x.tr(); 4677 if (strikethrough) { 4678 tr.style("text-decoration: line-through"); 4679 } 4680 addFirstCell(name, defRef, tr); 4681 tr.td().ah(context.prefixLocalHref(link)).tx(text); 4682 } 4683 } 4684 4685 private void addFirstCell(String name, String defRef, XhtmlNode tr) { 4686 var td = tr.td(); 4687 if (name.length() <= 16) { 4688 td.style("white-space: nowrap"); 4689 } 4690 if (defRef == null) { 4691 td.tx(name); 4692 } else if (Utilities.isAbsoluteUrl(defRef)) { 4693 td.ah(context.prefixLocalHref(defRef)).tx(name); 4694 } else { 4695 td.ah(context.prefixLocalHref(corePath+defRef)).tx(name); 4696 } 4697 } 4698 4699 private String head(String path) { 4700 if (path.contains(".")) 4701 return path.substring(0, path.indexOf(".")); 4702 else 4703 return path; 4704 } 4705 private String nottail(String path) { 4706 if (path.contains(".")) 4707 return path.substring(0, path.lastIndexOf(".")); 4708 else 4709 return path; 4710 } 4711 4712 private XhtmlNode businessIdWarning(String resource, String name) { 4713 if (name.equals("identifier")) { 4714 XhtmlNode ret = new XhtmlNode(NodeType.Element, "div"); 4715 ret.tx(context.formatPhrase(RenderingContext.STRUC_DEF_BUSINESS_ID)+" "); 4716 ret.ah(context.prefixLocalHref(corePath + "resource.html#identifiers")).tx(context.formatPhrase(RenderingContext.STRUC_DEF_DISCUSSION)); 4717 ret.tx(")"); 4718 return ret; 4719 } 4720 if (name.equals("version")) {// && !resource.equals("Device")) 4721 XhtmlNode ret = new XhtmlNode(NodeType.Element, "div"); 4722 ret.tx(context.formatPhrase(RenderingContext.STRUC_DEF_BUSINESS_VERID)+" "); 4723 ret.ah(context.prefixLocalHref(corePath + "resource.html#versions")).tx(context.formatPhrase(RenderingContext.STRUC_DEF_DISCUSSION)); 4724 ret.tx(")"); 4725 return ret; 4726 } 4727 return null; 4728 } 4729 4730 private XhtmlNode describeCardinality(ElementDefinition d, ElementDefinition compare, int mode) throws IOException { 4731 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 4732 if (compare==null || mode==GEN_MODE_DIFF) { 4733 if (!d.hasMax() && !d.hasMin()) 4734 return null; 4735 else if (d.getMax() == null) { 4736 renderStatus(d.getMinElement(), x).tx(toStr(d.getMin())); 4737 x.tx("..?"); 4738 } else { 4739 renderStatus(d.getMinElement(), x).tx(toStr(d.getMin())); 4740 x.tx( ".."); 4741 renderStatus(d.getMaxElement(), x).tx( d.getMax()); 4742 } 4743 } else { 4744 if (!(mode==GEN_MODE_DIFF && (d.getMin()==compare.getMin() || d.getMin()==0))) { 4745 compareString(x, toStr(d.getMin()), d.getMinElement(), null, "min", d, toStr(compare.getMin()), null, mode, false, false); 4746 } 4747 x.tx(".."); 4748 if (!(mode==GEN_MODE_DIFF && (d.getMax().equals(compare.getMax()) || "1".equals(d.getMax())))) { 4749 compareString(x, d.getMax(), d.getMaxElement(), null, "max", d, compare.getMax(), null, mode, false, false); 4750 } 4751 } 4752 XhtmlNode t = compareSimpleTypeLists(d.getCondition(), compare == null ? null : compare.getCondition(), mode); 4753 if (t != null) { 4754 x.br(); 4755 x.tx(context.formatPhrase(RenderingContext.STRUC_DEF_INVARIANT)+" "); 4756 x.copyAllContent(t); 4757 } 4758 return x; 4759 } 4760 4761 private boolean hasMustSupportTypes(List<TypeRefComponent> types) { 4762 for (TypeRefComponent tr : types) { 4763 if (isMustSupport(tr)) { 4764 return true; 4765 } 4766 } 4767 return false; 4768 } 4769 4770 private XhtmlNode describeTypes(List<TypeRefComponent> types, boolean mustSupportOnly, ElementDefinition ed, ElementDefinition compare, int mode, ElementDefinition value, ElementDefinition compareValue, StructureDefinition sd) throws FHIRException, IOException { 4771 if (types.isEmpty()) 4772 return null; 4773 4774 List<TypeRefComponent> compareTypes = compare==null ? new ArrayList<>() : compare.getType(); 4775 XhtmlNode ret = new XhtmlNode(NodeType.Element, "div"); 4776 if ((!mustSupportOnly && types.size() == 1 && compareTypes.size() <=1 && (mode != GEN_MODE_DIFF || !VersionComparisonAnnotation.hasDeleted(ed, "type"))) || (mustSupportOnly && mustSupportCount(types) == 1)) { 4777 if (!mustSupportOnly || isMustSupport(types.get(0))) { 4778 describeType(ret, types.get(0), mustSupportOnly, compareTypes.size()==0 ? null : compareTypes.get(0), mode, sd); 4779 } 4780 } else { 4781 boolean first = true; 4782 if (types.size() > 1) { 4783 ret.tx(context.formatPhrase(RenderingContext.STRUC_DEF_CHOICE_OF)+" "); 4784 } 4785 Map<String,TypeRefComponent> map = new HashMap<String, TypeRefComponent>(); 4786 for (TypeRefComponent t : compareTypes) { 4787 map.put(t.getCode(), t); 4788 } 4789 for (TypeRefComponent t : types) { 4790 TypeRefComponent compareType = map.get(t.getCode()); 4791 if (compareType!=null) 4792 map.remove(t.getCode()); 4793 if (!mustSupportOnly || isMustSupport(t)) { 4794 if (first) { 4795 first = false; 4796 } else { 4797 ret.tx(", "); 4798 } 4799 describeType(ret, t, mustSupportOnly, compareType, mode, sd); 4800 } 4801 } 4802 for (TypeRefComponent t : map.values()) { 4803 ret.tx(", "); 4804 describeType(removed(ret), t, mustSupportOnly, null, mode, sd); 4805 } 4806 if (mode == GEN_MODE_DIFF) { 4807 for (Base b : VersionComparisonAnnotation.getDeleted(ed, "type")) { 4808 TypeRefComponent t = (TypeRefComponent) b; 4809 ret.tx(", "); 4810 describeType(ret, t, false, null, mode, sd); 4811 } 4812 } 4813 } 4814 if (value != null) { 4815 XhtmlNode xt = processSecondary(mode, value, compareValue, mode, sd); 4816 if (xt != null) { 4817 ret.copyAllContent(xt); 4818 } 4819 } 4820 return ret; 4821 } 4822 4823 private XhtmlNode processSecondary(int mode, ElementDefinition value, ElementDefinition compareValue, int compMode, StructureDefinition sd) throws FHIRException, IOException { 4824 switch (mode) { 4825 case 1: 4826 return null; 4827 case 2: 4828 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 4829 x.tx(" "+context.formatPhrase(RenderingContext.STRUC_DEF_COMP_EX)); 4830 return x; 4831 case 3: 4832 x = new XhtmlNode(NodeType.Element, "div"); 4833 x.tx(" "+context.formatPhrase(RenderingContext.STRUC_DEF_EX_TYPE)+" "); 4834 x.copyAllContent(describeTypes(value.getType(), false, value, compareValue, compMode, null, null, sd)); 4835 x.tx(")"); 4836 return x; 4837 default: 4838 return null; 4839 } 4840 } 4841 4842 4843 private int mustSupportCount(List<TypeRefComponent> types) { 4844 int c = 0; 4845 for (TypeRefComponent tr : types) { 4846 if (isMustSupport(tr)) { 4847 c++; 4848 } 4849 } 4850 return c; 4851 } 4852 4853 4854 private void describeType(XhtmlNode x, TypeRefComponent t, boolean mustSupportOnly, TypeRefComponent compare, int mode, StructureDefinition sd) throws FHIRException, IOException { 4855 if (t.getWorkingCode() == null) { 4856 return; 4857 } 4858 if (t.getWorkingCode().startsWith("=")) { 4859 return; 4860 } 4861 4862 boolean ts = false; 4863 if (t.getWorkingCode().startsWith("xs:")) { 4864 ts = compareString(x, t.getWorkingCode(), t, null, "code", t, compare==null ? null : compare.getWorkingCode(), null, mode, false, false); 4865 } else { 4866 ts = compareString(x, t.getWorkingCode(), t, getTypeLink(t, sd), "code", t, compare==null ? null : compare.getWorkingCode(), compare==null ? null : getTypeLink(compare, sd), mode, false, false); 4867 } 4868 if (t.hasExtension(ExtensionDefinitions.EXT_TYPE_PARAMETER)) { 4869 x.tx("<"); 4870 boolean first = true; 4871 List<Extension> exl = t.getExtensionsByUrl(ExtensionDefinitions.EXT_TYPE_PARAMETER); 4872 for (Extension ex : exl) { 4873 if (first) { first = false; } else { x.tx("; "); } 4874 if (exl.size() > 1) { 4875 x.tx(ex.getExtensionString("name")); 4876 x.tx(":"); 4877 } 4878 String type = ex.getExtensionString("type"); 4879 StructureDefinition psd = context.getContext().fetchTypeDefinition(type); 4880 if (psd == null) { 4881 x.code().tx(type); 4882 } else if (psd.getWebPath() == null) { 4883 x.ah(context.prefixLocalHref(type)).tx(type); 4884 } else { 4885 x.ah(context.prefixLocalHref(psd.getWebPath())).tx(type); 4886 } 4887 } 4888 x.tx(">"); 4889 } 4890 if ((!mustSupportOnly && (t.hasProfile() || (compare!=null && compare.hasProfile()))) || isMustSupport(t.getProfile())) { 4891 StatusList<ResolvedCanonical> profiles = analyseProfiles(t.getProfile(), compare == null ? null : compare.getProfile(), mustSupportOnly, mode); 4892 if (profiles.size() > 0) { 4893 if (!ts) { 4894 getTypeLink(unchanged(x), t, sd); 4895 ts = true; 4896 } 4897 x.tx("("); 4898 boolean first = true; 4899 for (ResolvedCanonical rc : profiles) { 4900 if (first) first = false; else x.tx(", "); 4901 rc.render(x); 4902 } 4903 x.tx(")"); 4904 } 4905 } 4906 4907 if ((!mustSupportOnly && (t.hasTargetProfile() || (compare!=null && compare.hasTargetProfile()))) || isMustSupport(t.getTargetProfile())) { 4908 List<ResolvedCanonical> profiles = analyseProfiles(t.getTargetProfile(), compare == null ? null : compare.getTargetProfile(), mustSupportOnly, mode); 4909 if (profiles.size() > 0) { 4910 if (!ts) { 4911 getTypeLink(unchanged(x), t, sd); 4912 } 4913 x.tx("("); // todo: double use of "(" is problematic 4914 boolean first = true; 4915 for (ResolvedCanonical rc : profiles) { 4916 if (first) first = false; else x.tx(", "); 4917 rc.render(x); 4918 } 4919 x.tx(")"); 4920 } 4921 4922 if (!t.getAggregation().isEmpty() || (compare!=null && !compare.getAggregation().isEmpty())) { 4923 4924 for (Enumeration<AggregationMode> a :t.getAggregation()) { 4925 a.setUserData(UserDataNames.render_link, corePath + "codesystem-resource-aggregation-mode.html#content"); 4926 } 4927 if (compare!=null) { 4928 for (Enumeration<AggregationMode> a : compare.getAggregation()) { 4929 a.setUserData(UserDataNames.render_link, corePath + "codesystem-resource-aggregation-mode.html#content"); 4930 } 4931 } 4932 var xt = compareSimpleTypeLists(t.getAggregation(), compare == null ? null : compare.getAggregation(), mode); 4933 if (xt != null) { 4934 x.copyAllContent(xt); 4935 } 4936 } 4937 } 4938 } 4939 4940 private StatusList<ResolvedCanonical> analyseProfiles(List<CanonicalType> newProfiles, List<CanonicalType> oldProfiles, boolean mustSupportOnly, int mode) { 4941 StatusList<ResolvedCanonical> profiles = new StatusList<ResolvedCanonical>(); 4942 for (CanonicalType pt : newProfiles) { 4943 ResolvedCanonical rc = fetchProfile(pt, mustSupportOnly, null); 4944 profiles.add(rc); 4945 } 4946 if (oldProfiles!=null && mode != GEN_MODE_DIFF) { 4947 for (CanonicalType pt : oldProfiles) { 4948 profiles.merge(fetchProfile(pt, mustSupportOnly, null)); 4949 } 4950 } 4951 return profiles; 4952 } 4953 4954 private ResolvedCanonical fetchProfile(CanonicalType pt, boolean mustSupportOnly, StructureDefinition sd) { 4955 if (!pt.hasValue()) { 4956 return null; 4957 } 4958 if (!mustSupportOnly || isMustSupport(pt)) { 4959 StructureDefinition p = context.getProfileUtilities().findProfile(pt.getValue(), sd); 4960 return new ResolvedCanonical(pt.getValue(), p); 4961 } else { 4962 return null; 4963 } 4964 } 4965// 4966// private String getTypeProfile(CanonicalType pt, boolean mustSupportOnly) { 4967// StringBuilder b = new StringBuilder(); 4968// if (!mustSupportOnly || isMustSupport(pt)) { 4969// StructureDefinition p = context.getProfileUtilities().findProfile(pt.getValue()); 4970// if (p == null) 4971// b.append(pt.getValue()); 4972// else { 4973// String pth = p.getWebPath(); 4974// b.append("<a href=\"" + Utilities.escapeXml(pth) + "\" title=\"" + pt.getValue() + "\">"); 4975// b.append(p.getName()); 4976// b.append("</a>"); 4977// } 4978// } 4979// return b.toString(); 4980// } 4981 4982 private void getTypeLink(XhtmlNode x, TypeRefComponent t, StructureDefinition sd) { 4983 String s = context.getPkp().getLinkFor(sd.getWebPath(), t.getWorkingCode()); 4984 if (s != null) { 4985 x.ah(context.prefixLocalHref(s)).tx(t.getWorkingCode()); 4986 } else { 4987 x.code().tx(t.getWorkingCode()); 4988 } 4989 } 4990 4991 4992 private String getTypeLink(TypeRefComponent t, StructureDefinition sd) { 4993 String s = context.getPkp().getLinkFor(sd.getWebPath(), t.getWorkingCode()); 4994 return s; 4995 } 4996 4997 private XhtmlNode displayBoolean(boolean value, BooleanType source, String name, Base parent, BooleanType compare, int mode) { 4998 String newValue = value ? "true" : source.hasValue() ? "false" : null; 4999 String oldValue = compare==null || compare.getValue()==null ? null : (compare.getValue()!=true ? null : "true"); 5000 return compareString(newValue, source, null, name, parent, oldValue, null, mode, false, false); 5001 } 5002 5003 5004 private XhtmlNode invariants(List<ElementDefinitionConstraintComponent> originalList, List<ElementDefinitionConstraintComponent> compareList, ElementDefinition parent, int mode) throws IOException { 5005 StatusList<InvariantWithStatus> list = new StatusList<>(); 5006 for (ElementDefinitionConstraintComponent v : originalList) { 5007 if (!v.isEmpty()) { 5008 list.add(new InvariantWithStatus(v)); 5009 } 5010 } 5011 if (compareList != null && mode != GEN_MODE_DIFF) { 5012 for (ElementDefinitionConstraintComponent v : compareList) { 5013 list.merge(new InvariantWithStatus(v)); 5014 } 5015 } 5016 if (list.size() == 0) { 5017 return null; 5018 } 5019 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 5020 boolean first = true; 5021 for (InvariantWithStatus t : list) { 5022 if (first) first = false; else x.br(); 5023 t.render(x); 5024 } 5025 for (Base b : VersionComparisonAnnotation.getDeleted(parent, "invariant")) { 5026 if (first) first = false; else x.br(); 5027 InvariantWithStatus ts = new InvariantWithStatus((ElementDefinitionConstraintComponent) b); 5028 ts.render(x); 5029 } 5030 return x; 5031 } 5032 5033 private XhtmlNode describeBinding(StructureDefinition sd, ElementDefinition d, String path, ElementDefinition compare, int mode) throws FHIRException, IOException { 5034 if (!d.hasBinding()) 5035 return null; 5036 else { 5037 ElementDefinitionBindingComponent binding = d.getBinding(); 5038 ElementDefinitionBindingComponent compBinding = compare == null ? null : compare.getBinding(); 5039 XhtmlNode bindingDesc = null; 5040 if (binding.hasDescription()) { 5041 MarkdownType newBinding = PublicationHacker.fixBindingDescriptions(context.getContext(), binding.getDescriptionElement()); 5042 if (mode == GEN_MODE_SNAP || mode == GEN_MODE_MS) { 5043 bindingDesc = new XhtmlNode(NodeType.Element, "div"); 5044 bindingDesc.addChildren(new XhtmlParser().parseMDFragment(hostMd.processMarkdown("Binding.description", newBinding))); 5045 } else { 5046 5047 StringType oldBinding = compBinding != null && compBinding.hasDescription() ? PublicationHacker.fixBindingDescriptions(context.getContext(), compBinding.getDescriptionElement()) : null; 5048 bindingDesc = compareMarkdown("Binding.description", newBinding, oldBinding, mode); 5049 } 5050 } 5051 if (!binding.hasValueSet()) { 5052 return bindingDesc; 5053 } 5054 5055 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 5056 var nsp = x.span(); 5057 renderBinding(nsp, binding, compBinding, path, sd, mode); 5058 if (bindingDesc != null) { 5059 if (isSimpleContent(bindingDesc)) { 5060 x.tx(": "); 5061 x.copyAllContent(bindingDesc.getChildNodes().get(0)); 5062 } else { 5063 x.br(); 5064 x.copyAllContent(bindingDesc); 5065 } 5066 } 5067 if (binding.hasExtension(ExtensionDefinitions.EXT_CONCEPT_DOMAIN)) { 5068 x.tx(". "); 5069 x.tx(context.formatPhrase(RenderingI18nContext.SDR_CONCEPT_DOMAIN)); 5070 x.tx(": "); 5071 renderCoding(new RenderingStatus(), x, ResourceWrapper.forType(context.getContextUtilities(), binding.getExtensionByUrl(ExtensionDefinitions.EXT_CONCEPT_DOMAIN).getValue())); 5072 } 5073 5074 AdditionalBindingsRenderer abr = new AdditionalBindingsRenderer(context.getPkp(), corePath, sd, d.getPath(), context, hostMd, this); 5075 5076 if (binding.hasExtension(ExtensionDefinitions.EXT_MAX_VALUESET)) { 5077 abr.seeMaxBinding(ExtensionUtilities.getExtension(binding, ExtensionDefinitions.EXT_MAX_VALUESET), compBinding==null ? null : ExtensionUtilities.getExtension(compBinding, ExtensionDefinitions.EXT_MAX_VALUESET), mode!=GEN_MODE_SNAP && mode!=GEN_MODE_MS); 5078 } 5079 if (binding.hasExtension(ExtensionDefinitions.EXT_MIN_VALUESET)) { 5080 abr.seeMinBinding(ExtensionUtilities.getExtension(binding, ExtensionDefinitions.EXT_MIN_VALUESET), compBinding==null ? null : ExtensionUtilities.getExtension(compBinding, ExtensionDefinitions.EXT_MIN_VALUESET), mode!=GEN_MODE_SNAP && mode!=GEN_MODE_MS); 5081 } 5082 if (binding.hasExtension(ExtensionDefinitions.EXT_BINDING_ADDITIONAL)) { 5083 abr.seeAdditionalBindings(binding.getExtensionsByUrl(ExtensionDefinitions.EXT_BINDING_ADDITIONAL), compBinding==null ? null : compBinding.getExtensionsByUrl(ExtensionDefinitions.EXT_BINDING_ADDITIONAL), mode!=GEN_MODE_SNAP && mode!=GEN_MODE_MS); 5084 } 5085 5086 if (abr.hasBindings()) { 5087 var tbl = x.table("grid", false).markGenerated(!context.forValidResource()); 5088 abr.render(tbl.getChildNodes(), true); 5089 } 5090 return x; 5091 } 5092 } 5093 5094 private boolean isSimpleContent(XhtmlNode bindingDesc) { 5095 return bindingDesc.getChildNodes().size() == 1 && bindingDesc.getChildNodes().get(0).isPara(); 5096 } 5097 5098 private void renderBinding(XhtmlNode span, ElementDefinitionBindingComponent binding, ElementDefinitionBindingComponent compare, String path, StructureDefinition sd, int mode) { 5099 compareString(span, conf(binding), binding.getStrengthElement(), null, "strength", binding, compare == null ? null : conf(compare), null, mode, false, false); 5100 span.tx(" "); 5101 BindingResolution br = context.getPkp().resolveBinding(sd, binding, path); 5102 compareString(span, br.display, binding.getValueSetElement(), br.url, "valueSet", binding, compare == null ? null : compare.getValueSet(), null, mode, br.external, false); 5103 if (binding.hasStrength() || binding.hasValueSet()) { 5104 span.br(); 5105 span.tx("("); 5106 if (binding.hasStrength()) { 5107 span.ah(context.prefixLocalHref(Utilities.pathURL(corePath, "terminologies.html#"+binding.getStrength().toCode()))).tx(binding.getStrength().toCode()); 5108 } 5109 if (binding.hasStrength() && binding.hasValueSet()) { 5110 span.tx(" "); 5111 } 5112 if (binding.hasValueSet()) { 5113 span.tx("to "); 5114 XhtmlNode ispan = span.spanClss("copy-text-inline"); 5115 ispan.code().tx(binding.getValueSet()); 5116 ispan.button("btn-copy", context.formatPhrase(RenderingContext.STRUC_DEF_COPY_URL)).attribute("data-clipboard-text", binding.getValueSet()); 5117 } 5118 span.tx(")"); 5119 } 5120 } 5121 5122 private String stripPara(String s) { 5123 if (s.startsWith("<p>")) { 5124 s = s.substring(3); 5125 } 5126 if (s.trim().endsWith("</p>")) { 5127 s = s.substring(0, s.lastIndexOf("</p>")-1) + s.substring(s.lastIndexOf("</p>") +4); 5128 } 5129 return s; 5130 } 5131 5132 private String conf(ElementDefinitionBindingComponent def) { 5133 if (def.getStrength() == null) { 5134 return context.formatPhrase(RenderingContext.STRUC_DEF_FOR_CODE)+" "; 5135 } 5136 switch (def.getStrength()) { 5137 case EXAMPLE: 5138 return context.formatPhrase(RenderingContext.STRUC_DEF_EX_CODE)+" "; 5139 case PREFERRED: 5140 return context.formatPhrase(RenderingContext.STRUC_DEF_SHOULD_CODE)+" "; 5141 case EXTENSIBLE: 5142 return context.formatPhrase(RenderingContext.STRUC_DEF_SUIT_SHALL_CODE)+" "; 5143 case REQUIRED: 5144 return context.formatPhrase(RenderingContext.STRUC_DEF_SHALL_CODE)+" "; 5145 default: 5146 return "?sd-conf?"; 5147 } 5148 } 5149 5150 private XhtmlNode encodeValues(List<ElementDefinitionExampleComponent> examples) throws FHIRException, IOException { 5151 XhtmlNode x = null; 5152 for (ElementDefinitionExampleComponent ex : examples) { 5153 if (x == null) 5154 x = new XhtmlNode(NodeType.Element, "div"); 5155 else 5156 x.br(); 5157 XhtmlNode b = x.b(); 5158 b.tx(ex.getLabel()); 5159 b.tx(": "); 5160 x.tx(encodeValue(ex.getValue(), null)); 5161 } 5162 return x; 5163 5164 } 5165 5166 private XhtmlNode encodeValue(DataType value, String name, Base parent, DataType compare, int mode, String elementName) throws FHIRException, IOException { 5167 String oldValue = encodeValue(compare, elementName); 5168 String newValue = encodeValue(value, elementName); 5169 return compareString(newValue, value, null, name, parent, oldValue, null, mode, false, false, true); 5170 } 5171 5172 private String encodeValue(DataType value, String elementName) throws FHIRException, IOException { 5173 if (value == null || value.isEmpty()) { 5174 return null; 5175 } 5176 if (value instanceof PrimitiveType<?> && (context.getFixedFormat().notPrimitives() || elementName == null)) { 5177 return ((PrimitiveType<?>) value).asStringValue(); 5178 } 5179 5180 ByteArrayOutputStream bs = new ByteArrayOutputStream(); 5181 if (context.getFixedFormat().isXml()) { 5182 XmlParser parser = new XmlParser(); 5183 parser.setOutputStyle(OutputStyle.PRETTY); 5184 parser.compose(bs, value, null); 5185 } else if (value instanceof PrimitiveType<?>) { 5186 if (value instanceof BooleanType || value instanceof IntegerType || value instanceof DecimalType) { 5187 FileUtilities.stringToStream(((PrimitiveType<?>) value).asStringValue(), bs); 5188 } else { 5189 FileUtilities.stringToStream("\""+Utilities.escapeJson(((PrimitiveType<?>) value).asStringValue())+"\"", bs); 5190 } 5191 } else { 5192 JsonParser parser = new JsonParser(); 5193 parser.setOutputStyle(OutputStyle.PRETTY); 5194 parser.compose(bs, value, null); 5195 } 5196 String[] lines = bs.toString().split("\\r?\\n"); 5197 StringBuilder b = new StringBuilder(); 5198 for (String s : lines) { 5199 if (!Utilities.noString(s) && !s.startsWith("<?")) { // eliminate the xml header if it's xml 5200 b.append(s.replace(" xmlns=\"http://hl7.org/fhir\"", "")); 5201 b.append("\n"); 5202 } 5203 } 5204 boolean prefixWithName = context.getFixedFormat() == FixedValueFormat.JSON_ALL && elementName != null; 5205 if (elementName != null && elementName.contains("[x]")) { 5206 elementName = elementName.replace("[x]", Utilities.capitalize(value.fhirType())); 5207 } 5208 return (prefixWithName ? "\""+Utilities.escapeXml(elementName)+"\" : " : "")+ b.toString().trim(); 5209 } 5210 5211 private XhtmlNode getMapping(StructureDefinition profile, ElementDefinition d, String uri, ElementDefinition compare, int mode) { 5212 String id = null; 5213 for (StructureDefinitionMappingComponent m : profile.getMapping()) { 5214 if (m.hasUri() && m.getUri().equals(uri)) 5215 id = m.getIdentity(); 5216 } 5217 if (id == null) 5218 return null; 5219 String newMap = null; 5220 for (ElementDefinitionMappingComponent m : d.getMapping()) { 5221 if (m.getIdentity().equals(id)) { 5222 newMap = m.getMap(); 5223 break; 5224 } 5225 } 5226 if (Utilities.noString(newMap) && compare == null) { 5227 return null; 5228 } 5229 if (compare==null) 5230 return new XhtmlNode(NodeType.Element, "div").tx(newMap); 5231 String oldMap = null; 5232 for (ElementDefinitionMappingComponent m : compare.getMapping()) { 5233 if (m.getIdentity().equals(id)) { 5234 oldMap = m.getMap(); 5235 break; 5236 } 5237 } 5238 if (Utilities.noString(newMap) && Utilities.noString(oldMap)) { 5239 return null; 5240 } 5241 return compareString(Utilities.escapeXml(newMap), null, null, "mapping", d, Utilities.escapeXml(oldMap), null, mode, false, false); 5242 } 5243 5244 private XhtmlNode compareSimpleTypeLists(List<? extends PrimitiveType> original, List<? extends PrimitiveType> compare, int mode) throws IOException { 5245 return compareSimpleTypeLists(original, compare, mode, ", "); 5246 } 5247 5248 5249 private XhtmlNode compareSimpleTypeLists(List<? extends PrimitiveType> originalList, List<? extends PrimitiveType> compareList, int mode, String separator) throws IOException { 5250 StatusList<ValueWithStatus> list = new StatusList<>(); 5251 for (PrimitiveType v : originalList) { 5252 if (!v.isEmpty()) { 5253 list.add(new ValueWithStatus(v)); 5254 } 5255 } 5256 if (compareList != null && mode != GEN_MODE_DIFF) { 5257 for (PrimitiveType v : compareList) { 5258 list.merge(new ValueWithStatus(v)); 5259 } 5260 } 5261 if (list.size() == 0) { 5262 return null; 5263 } 5264 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 5265 boolean first = true; 5266 for (ValueWithStatus t : list) { 5267 if (first) first = false; else x.tx(separator); 5268 t.render(x); 5269 } 5270 return x; 5271 } 5272 5273 5274 private XhtmlNode compareDataTypeLists(List<? extends DataType> original, List<? extends DataType> compare, int mode) throws IOException { 5275 return compareDataTypeLists(original, compare, mode, ", "); 5276 } 5277 5278 5279 private XhtmlNode compareDataTypeLists(List<? extends DataType> originalList, List<? extends DataType> compareList, int mode, String separator) throws IOException { 5280 StatusList<DataValueWithStatus> list = new StatusList<>(); 5281 for (DataType v : originalList) { 5282 if (!v.isEmpty()) { 5283 list.add(new DataValueWithStatus(v, this, new RenderingStatus())); 5284 } 5285 } 5286 if (compareList != null && mode != GEN_MODE_DIFF) { 5287 for (DataType v : compareList) { 5288 list.merge(new DataValueWithStatus(v, this, new RenderingStatus())); 5289 } 5290 } 5291 if (list.size() == 0) { 5292 return null; 5293 } 5294 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 5295 boolean first = true; 5296 for (DataValueWithStatus t : list) { 5297 if (first) first = false; else x.tx(separator); 5298 t.render(x); 5299 } 5300 return x; 5301 } 5302 5303 5304 5305 private String summarise(CodeableConcept cc) throws FHIRException { 5306 if (cc.getCoding().size() == 1 && cc.getText() == null) { 5307 return summarise(cc.getCoding().get(0)); 5308 } else if (cc.hasText()) { 5309 return "\"" + cc.getText() + "\""; 5310 } else if (cc.getCoding().size() > 0) { 5311 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 5312 for (Coding c : cc.getCoding()) { 5313 b.append(summarise(c)); 5314 } 5315 return b.toString(); 5316 } else { 5317 throw new FHIRException(context.formatPhrase(RenderingContext.STRUC_DEF_ERR_DESC)); 5318 } 5319 } 5320 5321 private String summarise(Coding coding) throws FHIRException { 5322 if ("http://snomed.info/sct".equals(coding.getSystem())) 5323 return "" + (context.formatPhrase(RenderingContext.STRUC_DEF_SNOMED_CODE)) + " " + coding.getCode() + (!coding.hasDisplay() ? "" : "(\"" + gt(coding.getDisplayElement()) + "\")"); 5324 if ("http://loinc.org".equals(coding.getSystem())) 5325 return "" + (context.formatPhrase(RenderingContext.STRUC_DEF_LOINC_CODE)) + " " + coding.getCode() + (!coding.hasDisplay() ? "" : "(\"" + gt(coding.getDisplayElement()) + "\")"); 5326 if ("http://unitsofmeasure.org/".equals(coding.getSystem())) 5327 return " (" + (context.formatPhrase(RenderingContext.GENERAL_UCUM)) + ": " + coding.getCode() + ")"; 5328 CodeSystem cs = context.getContext().fetchCodeSystem(coding.getSystem()); 5329 if (cs == null) 5330 return "<span title=\"" + coding.getSystem() + "\">" + coding.getCode() + "</a>" + (!coding.hasDisplay() ? "" : "(\"" + gt(coding.getDisplayElement()) + "\")"); 5331 else 5332 return "<a title=\"" + cs.present() + "\" href=\"" + Utilities.escapeXml(cs.getWebPath()) + "#" + cs.getId() + "-" + coding.getCode() + "\">" + coding.getCode() + "</a>" + (!coding.hasDisplay() ? "" : "(\"" + gt(coding.getDisplayElement()) + "\")"); 5333 } 5334 5335 public XhtmlNode buildElementTable(RenderingStatus status, String defFile, StructureDefinition profile, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, 5336 boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean mustSupport, RenderingContext rc, String anchorPrefix, ResourceWrapper res) throws IOException { 5337 // in order to build this, we need to know the base type of the profile 5338 ElementTableGroupingEngine groupings = getElementTableGroupings(profile.getType()); 5339 if (groupings == null) { 5340 XhtmlNode node = new XhtmlNode(NodeType.Element, "div"); 5341 node.para().tx("This view is not supported for this profile because it is of an unsupported type"); 5342 return node; 5343 } else if (profile.getSnapshot().getElement().isEmpty()) { 5344 XhtmlNode node = new XhtmlNode(NodeType.Element, "div"); 5345 node.para().tx("This view is not supported for this profile because it doesn't have a snapshot"); 5346 return node; 5347 } else { 5348 List<ElementTable.TableGroup> groups = new ArrayList<ElementTable.TableGroup>(); 5349 List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, profile.getSnapshot().getElementFirstRep()); 5350 buildElementTableRows(profile, groupings, groups, children); 5351 5352 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, imageFolder, inlineGraphics, true, defFile, rc.getUniqueLocalPrefix()); 5353 gen.setTreelines(false); 5354 TableModel model = initElementTable(gen, corePath, true, profile.getId()+"e", true, TableGenerationMode.XHTML); 5355 new ElementTable(context, groups, this, ExtensionUtilities.hasExtensionValue(profile, ExtensionDefinitions.EXT_PROFILE_VIEW_HINT, "element-view-replace-cardinality")).build(gen, model); 5356 5357 try { 5358 return gen.generate(model, imagePath, 0, outputTracker); 5359 } catch (org.hl7.fhir.exceptions.FHIRException e) { 5360 throw new FHIRException(context.getWorker().formatMessage(I18nConstants.ERROR_GENERATING_TABLE_FOR_PROFILE__, profile.getUrl(), e.getMessage()), e); 5361 } 5362 } 5363 } 5364 5365 private void buildElementTableRows(StructureDefinition profile, ElementTableGroupingEngine groupings, List<TableGroup> groups, List<ElementDefinition> elements) { 5366 for (ElementDefinition ed : elements) { 5367 ElementTableGroupingState state = groupings.groupState(ed); 5368 ElementTable.TableGroup group = getOrMakeGroup(groupings, groups, ed); 5369 if (group != null) { 5370 boolean isLeaf = isLeaf(ed); 5371 if (isLeaf) { 5372 if (hasAnyDiff(profile, ed) && !ed.isProhibited()) { 5373 group.getElements().add(makeElement(profile, null, ed, true)); 5374 } 5375 } else if (state == ElementTableGroupingState.DEFINES_GROUP) { 5376 List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed); 5377 buildElementTableRows(profile, group, children, ed); 5378 } else if (hasAnyDiff(profile, ed) && !ed.isProhibited()) { 5379 TableElement e = makeElement(profile, null, ed, false); 5380 group.getElements().add(e); 5381 List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed); 5382 buildElementTableRows(profile, e, children); 5383 } 5384 } else if (!ed.isProhibited()) { 5385 // walk the children without creating anything. Will only do anything in a logical model 5386 List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed); 5387 buildElementTableRows(profile, groupings, groups, children); 5388 } 5389 } 5390 } 5391 5392 5393 private void buildElementTableRows(StructureDefinition profile, ElementTable.TableGroup group, List<ElementDefinition> elements, ElementDefinition parent) { 5394 for (ElementDefinition ed : elements) { 5395 if (hasAnyDiff(profile, ed) && !ed.isProhibited()) { 5396 boolean isLeaf = isLeaf(ed); 5397 if (isLeaf) { 5398 if (useParent(ed) ) { 5399 group.getElements().add(makeElement(profile, parent, ed, true)); 5400 } else { 5401 group.getElements().add(makeElement(profile, null, ed, true)); 5402 } 5403 } else { 5404 List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed); 5405 buildElementTableRows(profile, group, children, ed); 5406 } 5407 } 5408 } 5409 } 5410 5411 5412 private boolean useParent(ElementDefinition ed) { 5413 for (TypeRefComponent t : ed.getType()) { 5414 StructureDefinition sd = context.getContext().fetchTypeDefinition(t.getWorkingCode()); 5415 if (sd != null && sd.hasExtension(ExtensionDefinitions.EXT_PROFILE_VIEW_HINT)) { 5416 for (Extension ext : sd.getExtensionsByUrl(ExtensionDefinitions.EXT_PROFILE_VIEW_HINT)) { 5417 if (ext.hasValue()) { 5418 String v = ext.getValue().primitiveValue(); 5419 if ("element-view-defns-parent".equals(v)) { 5420 return true; 5421 } 5422 } 5423 } 5424 } 5425 } 5426 return true; 5427 } 5428 5429 private void buildElementTableRows(StructureDefinition profile, TableElement parent, List<ElementDefinition> elements) { 5430 for (ElementDefinition ed : elements) { 5431 if (hasAnyDiff(profile, ed) && !ed.isProhibited()) { 5432 boolean isLeaf = isLeaf(ed); 5433 if (isLeaf) { 5434 parent.getChildElements().add(makeElement(profile, null, ed, true)); 5435 } else { 5436 TableElement e = makeElement(profile, null, ed, false); 5437 parent.getChildElements().add(e); 5438 List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, ed); 5439 buildElementTableRows(profile, e, children); 5440 } 5441 } 5442 } 5443 } 5444 5445 private boolean isLeaf(ElementDefinition element) { 5446 for (TypeRefComponent t : element.getType()) { 5447 if (context.getContextUtilities().isDatatype(t.getWorkingCode()) && !Utilities.existsInList(t.getWorkingCode(), "Element", "BackboneElement")) { 5448 return true; 5449 } 5450 StructureDefinition sd = context.getContext().fetchTypeDefinition(t.getWorkingCode()); 5451 if (sd != null && sd.hasExtension(ExtensionDefinitions.EXT_PROFILE_VIEW_HINT)) { 5452 for (Extension ext : sd.getExtensionsByUrl(ExtensionDefinitions.EXT_PROFILE_VIEW_HINT)) { 5453 if (ext.hasValue()) { 5454 String v = ext.getValue().primitiveValue(); 5455 if ("element-view-as-leaf".equals(v)) { 5456 return true; 5457 } 5458 } 5459 } 5460 } 5461 } 5462 return false; 5463 } 5464 5465 private boolean hasAnyDiff(StructureDefinition profile, ElementDefinition e) { 5466 if (e.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF)) { 5467 ElementDefinition diff = (ElementDefinition) e.getUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF); 5468 return hasDefinitionalMaterial(diff); 5469 } 5470 for (ElementDefinition child : context.getProfileUtilities().getChildList(profile, e.getPath(), e.getId(), false, false)) { 5471 if (hasAnyDiff(profile, child)) { 5472 return true; 5473 } 5474 } 5475 return false; 5476 } 5477 5478 private boolean hasDefinitionalMaterial(ElementDefinition diff) { 5479 return diff.hasShort() || diff.hasDefinition() || diff.hasComment() || diff.hasRequirements() || 5480 diff.hasBinding() || diff.hasFixed() || diff.hasPattern() || diff.hasMin() || diff.hasMax() || 5481 diff.hasMinValue() || diff.hasMaxValue() || diff.hasConstraint() || diff.hasType() || diff.hasMaxLength(); 5482 } 5483 5484 private ElementTableGroupingEngine getElementTableGroupings(String type) throws JsonException, IOException { 5485 StructureDefinition sd = context.getContext().fetchTypeDefinition(type); 5486 if (sd == null) { 5487 return null; 5488 } 5489 if (sd.hasExtension(ExtensionDefinitions.EXT_PROFILE_VIEW_HINT) && "element-view-ready".equals(ExtensionUtilities.readStringExtension(sd, ExtensionDefinitions.EXT_PROFILE_VIEW_HINT))) { 5490 return new HintDrivenGroupingEngine(context.getProfileUtilities().getChildList(sd, sd.getSnapshot().getElementFirstRep())); 5491 } 5492 if (resourceGroupings == null) { 5493 byte[] cnt = context.getContext().getBinaryForKey("resource-groupings.json"); 5494 if (cnt != null) { 5495 resourceGroupings = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(cnt, true); 5496 } 5497 } 5498 if (resourceGroupings != null && resourceGroupings.has(type)) { 5499 return new JsonDrivenGroupingEngine(resourceGroupings.getJsonArray(type)); 5500 } else { 5501 return null; 5502 } 5503 } 5504 5505 private TableGroup getOrMakeGroup(ElementTableGroupingEngine groupings, List<TableGroup> groups, ElementDefinition element) { 5506 ElementTableGrouping grouping = groupings.getGroup(element); 5507 if (grouping == null) { 5508 return null; 5509 } 5510 for (TableGroup g : groups) { 5511 if (g.getDefinition().getKey() == grouping.getKey()) { 5512 return g; 5513 } 5514 } 5515 TableGroup g = new TableGroup(groups.size()+1, grouping); 5516 groups.add(g); 5517 return g; 5518 } 5519 5520 private TableElement makeElement(StructureDefinition profile, ElementDefinition parentDefn, ElementDefinition snapDefn, boolean lookAtChildren) { 5521 ElementDefinition working = parentDefn != null ? parentDefn : snapDefn; 5522 ElementDefinition diffDefn = (ElementDefinition) snapDefn.getUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF); 5523 ElementDefinition diffParentDefn = parentDefn == null ? null : (ElementDefinition) parentDefn.getUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF); 5524 ElementDefinition workingDiff = diffParentDefn != null ? diffParentDefn : diffDefn; 5525 5526 5527 TableElement e = new TableElement(working.getPath(), working.getShort(), Integer.toString(working.getMin()), working.getMax()); 5528 5529 if (snapDefn.getType().size() > 1) { 5530 List<String> list = new ArrayList<String>(); 5531 for (TypeRefComponent tr : snapDefn.getType()) { 5532 StructureDefinition sd = context.getContext().fetchTypeDefinition(tr.getWorkingCode()); 5533 list.add(sd == null ? tr.getWorkingCode() : sd.present()); 5534 } 5535 e.setType("Choice", null, "Choice of "+CommaSeparatedStringBuilder.join(",", list), "icon_choice.gif"); 5536 e.getConstraints().add(TableElementConstraint.makeTypes(TableElementConstraintType.CHOICE, null, snapDefn.getType(), profile)); 5537 } else { 5538 StructureDefinition sd = context.getContext().fetchTypeDefinition(snapDefn.getTypeFirstRep().getWorkingCode()); 5539 if (sd == null) { 5540 e.setType(snapDefn.getTypeFirstRep().getWorkingCode(), null, "Unknown Type", "icon_element.gif"); 5541 } else if (Utilities.existsInList(sd.getType(), "Element", "BackboneElement")) { 5542 e.setType("Group", sd.getWebPath(), sd.present(), chooseIcon(profile, snapDefn, snapDefn.getTypeFirstRep())); 5543 } else { 5544 e.setType(sd.present(), sd.getWebPath(), sd.present(), chooseIcon(profile, snapDefn, snapDefn.getTypeFirstRep())); 5545 } 5546 if (snapDefn.getTypeFirstRep().hasProfile()) { 5547 e.getConstraints().add(TableElementConstraint.makeList(TableElementConstraintType.PROFILE, null, snapDefn.getTypeFirstRep().getProfile(), profile)); 5548 } 5549 if (snapDefn.getTypeFirstRep().hasTargetProfile()) { 5550 e.getConstraints().add(TableElementConstraint.makeList(TableElementConstraintType.TARGET, null, snapDefn.getTypeFirstRep().getTargetProfile(), profile)); 5551 } 5552 } 5553 5554 if (working.hasDefinition()) { 5555 e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.DEFINITION, working.getDefinition())); 5556 } else if (snapDefn.hasDefinition()) { 5557 e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.DEFINITION, snapDefn.getDefinition())); 5558 } 5559 if (workingDiff != null && workingDiff.hasComment()) { 5560 e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.COMMENT, workingDiff.getComment())); 5561 } else if (diffDefn != null && diffDefn.hasComment()) { 5562 e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.COMMENT, diffDefn.getComment())); 5563 } 5564 if (workingDiff != null && workingDiff.hasRequirements()) { 5565 e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.REQUIREMENTS, workingDiff.getRequirements())); 5566 } else if (diffDefn != null && diffDefn.hasRequirements()) { 5567 e.getDefinitions().add(new TableElementDefinition(TableElementDefinitionType.REQUIREMENTS, diffDefn.getRequirements())); 5568 } 5569 5570 checkValueDomainConstraints(snapDefn, diffDefn, null, e, false, profile); 5571 if (lookAtChildren) { 5572 List<ElementDefinition> children = context.getProfileUtilities().getChildList(profile, snapDefn); 5573 for (ElementDefinition child : children) { 5574 checkValueDomainConstraints(child, (ElementDefinition) child.getUserData(UserDataNames.SNAPSHOT_DERIVATION_DIFF), child.getName(), e, true, profile); 5575 } 5576 } 5577 return e; 5578 } 5579 5580 public void checkValueDomainConstraints(ElementDefinition defn, ElementDefinition diffDefn, String path, TableElement e, boolean cardinality, StructureDefinition sd) { 5581 if (cardinality) { 5582 if (defn.getBase().getMin() != defn.getMin() || !defn.getBase().getMax().equals(defn.getMax())) { 5583 e.getConstraints().add(TableElementConstraint.makeRange(TableElementConstraintType.CARDINALITY, path, defn.getMinElement(), defn.getMaxElement(), sd)); 5584 } 5585 } 5586 // ok, now we collect constraints on the value domain, which maybe in sub-elements, though at some point we give up 5587 if (defn.hasFixed()) { 5588 e.getConstraints().add(TableElementConstraint.makeValueVS(TableElementConstraintType.FIXED, path, defn.getFixed(), defn.getBinding().getStrength(), defn.getBinding().getValueSet(), sd)); 5589 } 5590 if (defn.hasPattern()) { 5591 e.getConstraints().add(TableElementConstraint.makeValueVS(TableElementConstraintType.PATTERN, path, defn.getPattern(), defn.getBinding().getStrength(), defn.getBinding().getValueSet(), sd)); 5592 } 5593 if (defn.hasMinValue() || defn.hasMaxValue()) { 5594 e.getConstraints().add(TableElementConstraint.makeRange(TableElementConstraintType.RANGE, path, defn.getMinValue(), defn.getMaxValue(), sd)); 5595 } 5596 if (defn.hasMaxLength()) { 5597 e.getConstraints().add(TableElementConstraint.makeValue(TableElementConstraintType.MAXLENGTH, path, defn.getMaxLengthElement(), sd)); 5598 } 5599 if (defn.hasBinding() && defn.getBinding().hasValueSet() && (!cardinality || (diffDefn != null && diffDefn.hasBinding())) && !defn.hasFixedOrPattern()) { 5600 e.getConstraints().add(TableElementConstraint.makeBinding(TableElementConstraintType.BINDING, path, defn.getBinding().getStrength(), defn.getBinding().getValueSet(), sd)); 5601 } 5602 } 5603 5604 private String chooseIcon(StructureDefinition profile, ElementDefinition element, TypeRefComponent tr) { 5605 5606 if (tail(element.getPath()).equals("extension") && isExtension(element)) { 5607 if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue(), profile)) 5608 return "icon_extension_complex.png"; 5609 else 5610 return "icon_extension_simple.png"; 5611 } else if (tail(element.getPath()).equals("modifierExtension")) { 5612 if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue(), profile)) 5613 return "icon_modifier_extension_complex.png"; 5614 else 5615 return "icon_modifier_extension_simple.png"; 5616 } else if (element.getType().size() == 0) { 5617 if (profile != null && context.getWorker().getResourceNames().contains(profile.getType())) { 5618 return "icon_resource.png"; 5619 } else if (element.hasExtension(ExtensionDefinitions.EXT_JSON_PROP_KEY)) { 5620 return "icon-object-box.png"; 5621 } else { 5622 return "icon_element.gif"; 5623 } 5624 } else if (element.getType().size() > 1) { 5625 if (allAreReference(element.getType())) { 5626 return "icon_reference.png"; 5627 } else if (element.hasExtension(ExtensionDefinitions.EXT_JSON_PRIMITIVE_CHOICE)) { 5628 return "icon_choice.gif"; 5629 } else { 5630 return "icon_choice.gif"; 5631 } 5632 } else if (element.getType().get(0).getWorkingCode() != null && element.getType().get(0).getWorkingCode().startsWith("@")) { 5633 return "icon_reuse.png"; 5634 } else if (context.getContext().isPrimitiveType(element.getType().get(0).getWorkingCode())) { 5635 if (keyRows.contains(element.getId())) { 5636 return "icon-key.png"; 5637 } else { 5638 return "icon_primitive.png"; 5639 } 5640 } else if (element.getType().get(0).hasTarget()) { 5641 return "icon_reference.png"; 5642 } else if (Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Element", "BackboneElement")) { 5643 return "icon_group.png"; 5644 } else if (Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Base", "Element", "BackboneElement")) { 5645 return "icon_element.gif"; 5646 } else if (context.getContext().isDataType(element.getType().get(0).getWorkingCode())) { 5647 return "icon_datatype.gif"; 5648 } else if (element.hasExtension(ExtensionDefinitions.EXT_JSON_PROP_KEY)) { 5649 return "icon-object-box.png"; 5650 } else { 5651 return "icon_resource.png"; 5652 } 5653 5654 } 5655 5656 public MapStructureMode getMappingsMode() { 5657 return mappingsMode; 5658 } 5659 5660 public void setMappingsMode(MapStructureMode mappingsMode) { 5661 this.mappingsMode = mappingsMode; 5662 } 5663 5664 public List<StructureDefinition> getMappingTargets() { 5665 return mappingTargets; 5666 } 5667 5668}