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