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