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