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