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