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