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