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