001package org.hl7.fhir.r5.renderers; 002 003import java.io.IOException; 004import java.io.UnsupportedEncodingException; 005import java.util.ArrayList; 006import java.util.HashSet; 007import java.util.List; 008import java.util.Set; 009 010import org.apache.commons.lang3.StringUtils; 011import org.hl7.fhir.exceptions.DefinitionException; 012import org.hl7.fhir.exceptions.FHIRException; 013import org.hl7.fhir.exceptions.FHIRFormatError; 014import org.hl7.fhir.r5.conformance.AdditionalBindingsRenderer; 015import org.hl7.fhir.r5.conformance.profile.BindingResolution; 016import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; 017import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 018import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ElementChoiceGroup; 019import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ExtensionContext; 020import org.hl7.fhir.r5.context.IWorkerContext; 021import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; 022import org.hl7.fhir.r5.formats.IParser; 023import org.hl7.fhir.r5.formats.JsonParser; 024import org.hl7.fhir.r5.model.ActorDefinition; 025import org.hl7.fhir.r5.model.Base; 026import org.hl7.fhir.r5.model.CanonicalType; 027import org.hl7.fhir.r5.model.CodeType; 028import org.hl7.fhir.r5.model.CodeableConcept; 029import org.hl7.fhir.r5.model.Coding; 030import org.hl7.fhir.r5.model.DataType; 031import org.hl7.fhir.r5.model.Element; 032import org.hl7.fhir.r5.model.ElementDefinition; 033import org.hl7.fhir.r5.model.ElementDefinition.AdditionalBindingPurposeVS; 034import org.hl7.fhir.r5.model.ElementDefinition.AggregationMode; 035import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; 036import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; 037import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; 038import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; 039import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionExampleComponent; 040import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; 041import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionObligationComponent; 042import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent; 043import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; 044import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation; 045import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; 046import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 047import org.hl7.fhir.r5.model.Enumeration; 048import org.hl7.fhir.r5.model.Extension; 049import org.hl7.fhir.r5.model.IdType; 050import org.hl7.fhir.r5.model.IntegerType; 051import org.hl7.fhir.r5.model.PrimitiveType; 052import org.hl7.fhir.r5.model.Quantity; 053import org.hl7.fhir.r5.model.Resource; 054import org.hl7.fhir.r5.model.StringType; 055import org.hl7.fhir.r5.model.StructureDefinition; 056import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 057import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent; 058import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; 059import org.hl7.fhir.r5.model.UriType; 060import org.hl7.fhir.r5.model.UsageContext; 061import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; 062import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution; 063import org.hl7.fhir.r5.renderers.utils.RenderingContext; 064import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 065import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; 066import org.hl7.fhir.r5.renderers.utils.RenderingContext.StructureDefinitionRendererMode; 067import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; 068import org.hl7.fhir.r5.utils.PublicationHacker; 069import org.hl7.fhir.r5.utils.ToolingExtensions; 070import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 071import org.hl7.fhir.utilities.MarkDownProcessor; 072import org.hl7.fhir.utilities.StandardsStatus; 073import org.hl7.fhir.utilities.Utilities; 074import org.hl7.fhir.utilities.VersionUtilities; 075import org.hl7.fhir.utilities.i18n.I18nConstants; 076import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; 077import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; 078import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; 079import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; 080import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; 081import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Title; 082import org.hl7.fhir.utilities.xhtml.NodeType; 083import org.hl7.fhir.utilities.xhtml.XhtmlNode; 084import org.hl7.fhir.utilities.xhtml.XhtmlNodeList; 085 086public class StructureDefinitionRenderer extends ResourceRenderer { 087 088 private List<String> keyRows = new ArrayList<>(); 089 090 public StructureDefinitionRenderer(RenderingContext context) { 091 super(context); 092 } 093 094 public StructureDefinitionRenderer(RenderingContext context, ResourceContext rcontext) { 095 super(context, rcontext); 096 } 097 098 public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException { 099 return render(x, (StructureDefinition) dr); 100 } 101 102 public boolean render(XhtmlNode x, StructureDefinition sd) throws FHIRFormatError, DefinitionException, IOException { 103 x.getChildNodes().add(generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, 104 context.getLink(KnownLinkType.SPEC), "", sd.getKind() == StructureDefinitionKind.LOGICAL, false, null, false, context, "")); 105 return true; 106 } 107 108 public void describe(XhtmlNode x, StructureDefinition sd) { 109 x.tx(display(sd)); 110 } 111 112 public String display(StructureDefinition sd) { 113 return sd.present(); 114 } 115 116 @Override 117 public String display(Resource r) throws UnsupportedEncodingException, IOException { 118 return ((StructureDefinition) r).present(); 119 } 120 121 public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException { 122 if (r.has("title")) { 123 return r.children("title").get(0).getBase().primitiveValue(); 124 } 125 if (r.has("name")) { 126 return r.children("name").get(0).getBase().primitiveValue(); 127 } 128 return "??"; 129 } 130 131 132// private static final int AGG_NONE = 0; 133// private static final int AGG_IND = 1; 134// private static final int AGG_GR = 2; 135// private static final boolean TABLE_FORMAT_FOR_FIXED_VALUES = false; 136 public static final String CONSTRAINT_CHAR = "C"; 137 public static final String CONSTRAINT_STYLE = "padding-left: 3px; padding-right: 3px; border: 1px maroon solid; font-weight: bold; color: #301212; background-color: #fdf4f4;"; 138 private final boolean ADD_REFERENCE_TO_TABLE = true; 139 140 private boolean useTableForFixedValues = true; 141 142 public static class UnusedTracker { 143 private boolean used; 144 } 145 146 private class SpanEntry { 147 private List<SpanEntry> children = new ArrayList<SpanEntry>(); 148 private boolean profile; 149 private String id; 150 private String name; 151 private String resType; 152 private String cardinality; 153 private String description; 154 private String profileLink; 155 private String resLink; 156 private String type; 157 158 public String getName() { 159 return name; 160 } 161 public void setName(String name) { 162 this.name = name; 163 } 164 public String getResType() { 165 return resType; 166 } 167 public void setResType(String resType) { 168 this.resType = resType; 169 } 170 public String getCardinality() { 171 return cardinality; 172 } 173 public void setCardinality(String cardinality) { 174 this.cardinality = cardinality; 175 } 176 public String getDescription() { 177 return description; 178 } 179 public void setDescription(String description) { 180 this.description = description; 181 } 182 public String getProfileLink() { 183 return profileLink; 184 } 185 public void setProfileLink(String profileLink) { 186 this.profileLink = profileLink; 187 } 188 public String getResLink() { 189 return resLink; 190 } 191 public void setResLink(String resLink) { 192 this.resLink = resLink; 193 } 194 public String getId() { 195 return id; 196 } 197 public void setId(String id) { 198 this.id = id; 199 } 200 public boolean isProfile() { 201 return profile; 202 } 203 public void setProfile(boolean profile) { 204 this.profile = profile; 205 } 206 public List<SpanEntry> getChildren() { 207 return children; 208 } 209 public String getType() { 210 return type; 211 } 212 public void setType(String type) { 213 this.type = type; 214 } 215 216 } 217 218 private class ElementInStructure { 219 220 private StructureDefinition source; 221 private ElementDefinition element; 222 223 public ElementInStructure(StructureDefinition source, ElementDefinition ed) { 224 this.source = source; 225 this.element = ed; 226 } 227 228 public StructureDefinition getSource() { 229 return source; 230 } 231 232 public ElementDefinition getElement() { 233 return element; 234 } 235 236 } 237 private ElementInStructure getElementByName(List<ElementDefinition> elements, String contentReference, StructureDefinition source) { 238 if (contentReference.contains("#")) { 239 String url = contentReference.substring(0, contentReference.indexOf("#")); 240 contentReference = contentReference.substring(contentReference.indexOf("#")); 241 if (Utilities.noString(url)) { 242 url = source.getUrl(); 243 } 244 if (!url.equals(source.getUrl())) { 245 source = context.getWorker().fetchResource(StructureDefinition.class, url, source); 246 if (source == null) { 247 throw new FHIRException("Unable to resolve StructureDefinition "+url+" resolving content reference "+contentReference); 248 } 249 elements = source.getSnapshot().getElement(); 250 } 251 } 252 for (ElementDefinition ed : elements) { 253 if (("#"+ed.getPath()).equals(contentReference)) { 254 return new ElementInStructure(source, ed); 255 } 256 if (("#"+ed.getId()).equals(contentReference)) { 257 return new ElementInStructure(source, ed); 258 } 259 } 260 throw new Error("getElementByName: can't find "+contentReference+" in "+elements.toString()+" from "+source.getUrl()); 261// return null; 262 } 263 264 public XhtmlNode generateGrid(String defFile, StructureDefinition profile, String imageFolder, boolean inlineGraphics, String profileBaseFileName, String corePath, String imagePath, Set<String> outputTracker) throws IOException, FHIRException { 265 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); 266 gen.setTranslator(getTranslator()); 267 TableModel model = gen.initGridTable(corePath, profile.getId()); 268 List<ElementDefinition> list = profile.getSnapshot().getElement(); 269 List<StructureDefinition> profiles = new ArrayList<StructureDefinition>(); 270 profiles.add(profile); 271 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)); 272 try { 273 return gen.generate(model, imagePath, 1, outputTracker); 274 } catch (org.hl7.fhir.exceptions.FHIRException e) { 275 throw new FHIRException(e.getMessage(), e); 276 } 277 } 278 279 280 private static class Column { 281 String id; 282 String title; 283 String hint; 284 private String link; 285 286 protected Column(String id, String title, String hint) { 287 super(); 288 this.id = id; 289 this.title = title; 290 this.hint = hint; 291 } 292 protected Column(String id, String title, String hint, String link) { 293 super(); 294 this.id = id; 295 this.title = title; 296 this.hint = hint; 297 this.link = link; 298 } 299 300 } 301 public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, 302 boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean mustSupport, RenderingContext rc, String anchorPrefix) throws IOException, FHIRException { 303 assert(diff != snapshot);// check it's ok to get rid of one of these 304 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); 305 gen.setTranslator(getTranslator()); 306 307 List<ElementDefinition> list; 308 if (diff) 309 list = supplementMissingDiffElements(profile); 310 else { 311 list = new ArrayList<>(); 312 list.addAll(profile.getSnapshot().getElement()); 313 } 314 315 List<Column> columns = new ArrayList<>(); 316 TableModel model; 317 switch (context.getStructureMode()) { 318 case BINDINGS: 319 scanBindings(columns, list); 320 model = initCustomTable(gen, corePath, false, true, profile.getId()+(diff ? "d" : "s"), rc.getRules() == GenerationRules.IG_PUBLISHER, columns); 321 break; 322 case OBLIGATIONS: 323 scanObligations(columns, list); 324 model = initCustomTable(gen, corePath, false, true, profile.getId()+(diff ? "d" : "s"), rc.getRules() == GenerationRules.IG_PUBLISHER, columns); 325 break; 326 case SUMMARY: 327 model = gen.initNormalTable(corePath, false, true, profile.getId()+(diff ? "d" : "s"), rc.getRules() == GenerationRules.IG_PUBLISHER); 328 break; 329 default: 330 throw new Error("Unknown structure mode"); 331 } 332 333 List<StructureDefinition> profiles = new ArrayList<StructureDefinition>(); 334 profiles.add(profile); 335 keyRows.clear(); 336 337 genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null, mustSupport, rc, anchorPrefix, profile, columns); 338 try { 339 return gen.generate(model, imagePath, 0, outputTracker); 340 } catch (org.hl7.fhir.exceptions.FHIRException e) { 341 throw new FHIRException(context.getWorker().formatMessage(I18nConstants.ERROR_GENERATING_TABLE_FOR_PROFILE__, profile.getUrl(), e.getMessage()), e); 342 } 343 } 344 345 private void scanBindings(List<Column> columns, List<ElementDefinition> list) { 346 Set<String> cols = new HashSet<>(); 347 scanBindings(cols, list, list.get(0)); 348 if (cols.contains("required")) { 349 columns.add(new Column("required", "Required", "Concepts must come from this value set")); 350 } 351 if (cols.contains("extensible")) { 352 columns.add(new Column("extensible", "Extensible", "Concepts must come from this value set if an appropriate concept is in the value set ")); 353 } 354 if (cols.contains("maximum")) { 355 columns.add(new Column("maximum", "Maximum", "A required binding for additional codes, for use when the binding strength is 'extensible' or 'preferred'")); 356 } 357 if (cols.contains("minimum")) { 358 columns.add(new Column("minimum", "Minimum", "The minimum allowable value set - any conformant system SHALL support all these codes")); 359 } 360 if (cols.contains("candidate")) { 361 columns.add(new Column("candidate", "Candidate", "This value set is a candidate to substitute for the overall conformance value set in some situations; usually these are defined in the documentation")); 362 } 363 if (cols.contains("current")) { 364 columns.add(new Column("current", "Current", "New records are required to use this value set, but legacy records may use other codes. The definition of 'new record' is difficult, since systems often create new records based on pre-existing data. Usually 'current' bindings are mandated by an external authority that makes clear rules around this")); 365 } 366 if (cols.contains("preferred")) { 367 columns.add(new Column("preferred", "Preferred", "This is the value set that is preferred in a given context (documentation should explain why)")); 368 } 369 if (cols.contains("ui")) { 370 columns.add(new Column("ui", "UI", "This value set is provided for user look up in a given context. Typically, these valuesets only include a subset of codes relevant for input in a context")); 371 } 372 if (cols.contains("starter")) { 373 columns.add(new Column("starter", "Starter", "This value set is a good set of codes to start with when designing your system")); 374 } 375 if (cols.contains("component")) { 376 columns.add(new Column("component", "Component", "This value set is a component of the base value set. Usually this is called out so that documentation can be written about a portion of the value set")); 377 } 378 if (cols.contains("example")) { 379 columns.add(new Column("example", "Example", "Instances are not expected or even encouraged to draw from the specified value set. The value set merely provides examples of the types of concepts intended to be included.")); 380 } 381 } 382 383 public void scanBindings(Set<String> cols, List<ElementDefinition> list, ElementDefinition ed) { 384 if (ed.hasBinding()) { 385 if (ed.getBinding().hasValueSet() && ed.getBinding().hasStrength()) { 386 switch (ed.getBinding().getStrength()) { 387 case EXAMPLE: 388 cols.add("example"); 389 break; 390 case EXTENSIBLE: 391 cols.add("extensible"); 392 break; 393 case PREFERRED: 394 cols.add("preferred"); 395 break; 396 case REQUIRED: 397 cols.add("required"); 398 break; 399 default: 400 break; 401 } 402 } 403 for (ElementDefinitionBindingAdditionalComponent ab : ed.getBinding().getAdditional()) { 404 cols.add(ab.getPurpose().toCode()); 405 } 406 for (Extension ext : ed.getBinding().getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)) { 407 cols.add(ext.getExtensionString("purpose")); 408 } 409 } 410 411 List<ElementDefinition> children = getChildren(list, ed); 412 for (ElementDefinition element : children) { 413 scanBindings(cols, list, element); 414 } 415 } 416 417 private void scanObligations(List<Column> columns, List<ElementDefinition> list) { 418 Set<String> cols = new HashSet<>(); 419 scanObligations(cols, list, list.get(0)); 420 421 if (cols.contains("$all")) { 422 columns.add(new Column("$all", "All Actors", "Obligations that apply to all actors")); 423 } 424 for (String col : cols) { 425 if (!"$all".equals(col)) { 426 ActorDefinition actor = context.getWorker().fetchResource(ActorDefinition.class, col); 427 if (actor == null) { 428 columns.add(new Column(col, tail(col), "Obligations that apply to the undefined actor "+col, col)); 429 } else { 430 columns.add(new Column(col, actor.getName(), "Obligations that apply to the actor "+actor.present(), actor.getUserString("path"))); 431 } 432 } 433 } 434 } 435 436 private void scanObligations(Set<String> cols, List<ElementDefinition> list, ElementDefinition ed) { 437 438 for (ElementDefinitionObligationComponent ob : ed.getObligation()) { 439 if (ob.hasActor()) { 440 for (CanonicalType a : ob.getActor()) { 441 cols.add(a.getValue()); 442 } 443 } else 444 cols.add("$all"); 445 } 446 447 List<ElementDefinition> children = getChildren(list, ed); 448 for (ElementDefinition element : children) { 449 scanObligations(cols, list, element); 450 } 451 } 452 453 public TableModel initCustomTable(HierarchicalTableGenerator gen, String prefix, boolean isLogical, boolean alternating, String id, boolean isActive, List<Column> columns) { 454 TableModel model = gen.new TableModel(id, isActive); 455 456 model.setAlternating(alternating); 457 model.setDocoImg(Utilities.pathURL(prefix, "help16.png")); 458 model.setDocoRef(Utilities.pathURL("https://build.fhir.org/ig/FHIR/ig-guidance", "readingIgs.html#table-views")); 459 model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", "Name"), translate("sd.hint", "The logical name of the element"), null, 0)); 460 for (Column col : columns) { 461 model.getTitles().add(gen.new Title(null, model.getDocoRef(), translate("sd.head", col.title), translate("sd.hint", col.hint), null, 0)); 462 } 463 return model; 464 } 465 466 private Row genElement(String defPath, HierarchicalTableGenerator gen, List<Row> rows, ElementDefinition element, List<ElementDefinition> all, List<StructureDefinition> profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, 467 boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport, RenderingContext rc, String anchorPrefix, Resource srcSD, List<Column> columns) throws IOException, FHIRException { 468 Row originalRow = slicingRow; 469 StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); 470 Row typesRow = null; 471 472 List<ElementDefinition> children = getChildren(all, element); 473// if (!snapshot && isExtension && extensions != null && extensions != isExtension) 474// return; 475 476 if (!onlyInformationIsMapping(all, element)) { 477 Row row = gen.new Row(); 478 row.setAnchor(element.getPath()); 479 row.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); 480 if (element.hasSlicing()) 481 row.setLineColor(1); 482 else if (element.hasSliceName()) 483 row.setLineColor(2); 484 else 485 row.setLineColor(0); 486 boolean hasDef = element != null; 487 boolean ext = false; 488 if (tail(element.getPath()).equals("extension")) { 489 if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue())) 490 row.setIcon("icon_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); 491 else 492 row.setIcon("icon_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); 493 ext = true; 494 } else if (tail(element.getPath()).equals("modifierExtension")) { 495 if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue())) 496 row.setIcon("icon_modifier_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); 497 else 498 row.setIcon("icon_modifier_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); 499 } else if (!hasDef || element.getType().size() == 0) { 500 if (root && profile != null && context.getWorker().getResourceNames().contains(profile.getType())) { 501 row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); 502 } else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { 503 row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX); 504 keyRows.add(element.getId()+"."+ToolingExtensions.readStringExtension(element, ToolingExtensions.EXT_JSON_PROP_KEY)); 505 } else { 506 row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); 507 } 508 } else if (hasDef && element.getType().size() > 1) { 509 if (allAreReference(element.getType())) { 510 row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); 511 } else if (element.hasExtension(ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE)) { 512 row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); 513 } else { 514 row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); 515 typesRow = row; 516 } 517 } else if (hasDef && element.getType().get(0).getWorkingCode() != null && element.getType().get(0).getWorkingCode().startsWith("@")) { 518 row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE); 519 } else if (hasDef && isPrimitive(element.getType().get(0).getWorkingCode())) { 520 if (keyRows.contains(element.getId())) { 521 row.setIcon("icon-key.png", HierarchicalTableGenerator.TEXT_ICON_KEY); 522 } else { 523 row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); 524 } 525 } else if (hasDef && element.getType().get(0).hasTarget()) { 526 row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); 527 } else if (hasDef && isDataType(element.getType().get(0).getWorkingCode())) { 528 row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); 529 } else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { 530 row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX); 531 keyRows.add(element.getId()+"."+ToolingExtensions.readStringExtension(element, ToolingExtensions.EXT_JSON_PROP_KEY)); 532 } else if (hasDef && Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Base", "Element", "BackboneElement")) { 533 row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); 534 } else { 535 row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); 536 } 537 if (element.hasUserData("render.opaque")) { 538 row.setOpacity("0.5"); 539 } 540 UnusedTracker used = new UnusedTracker(); 541 String ref = defPath == null ? null : defPath + anchorPrefix + element.getId(); 542 String sName = tail(element.getPath()); 543 if (element.hasSliceName()) 544 sName = sName +":"+element.getSliceName(); 545 used.used = true; 546 if (logicalModel && element.hasRepresentation(PropertyRepresentation.XMLATTR)) 547 sName = "@"+sName; 548 Cell nc = genElementNameCell(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, all); 549 switch (context.getStructureMode()) { 550 case BINDINGS: 551 genElementBindings(gen, element, columns, row, profile, corePath); 552 break; 553 case OBLIGATIONS: 554 genElementObligations(gen, element, columns, row); 555 break; 556 case SUMMARY: 557 genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true, rc); 558 break; 559 560 } 561 if (element.hasSlicing()) { 562 if (standardExtensionSlicing(element)) { 563 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(); 564 showMissing = false; //? 565 } else { 566 row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); 567 slicingRow = row; 568 for (Cell cell : row.getCells()) 569 for (Piece p : cell.getPieces()) { 570 p.addStyle("font-style: italic"); 571 } 572 } 573 } else if (element.hasSliceName()) { 574 row.setIcon("icon_slice_item.png", HierarchicalTableGenerator.TEXT_ICON_SLICE_ITEM); 575 } 576 if (used.used || showMissing) 577 rows.add(row); 578 if (!used.used && !element.hasSlicing()) { 579 for (Cell cell : row.getCells()) 580 for (Piece p : cell.getPieces()) { 581 p.setStyle("text-decoration:line-through"); 582 p.setReference(null); 583 } 584 } else { 585 if (slicingRow != originalRow && !children.isEmpty()) { 586 // we've entered a slice; we're going to create a holder row for the slice children 587 Row hrow = gen.new Row(); 588 hrow.setAnchor(element.getPath()); 589 hrow.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); 590 hrow.setLineColor(1); 591 hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); 592 hrow.getCells().add(gen.new Cell(null, null, sName+":All Slices", "", null)); 593 switch (context.getStructureMode()) { 594 case BINDINGS: 595 case OBLIGATIONS: 596 for (Column col : columns) { 597 hrow.getCells().add(gen.new Cell()); 598 } 599 break; 600 case SUMMARY: 601 hrow.getCells().add(gen.new Cell()); 602 hrow.getCells().add(gen.new Cell()); 603 hrow.getCells().add(gen.new Cell()); 604 hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all slices", "", null)); 605 break; 606 } 607 row.getSubRows().add(hrow); 608 row = hrow; 609 } 610 if (typesRow != null && !children.isEmpty()) { 611 // we've entered a typing slice; we're going to create a holder row for the all types children 612 Row hrow = gen.new Row(); 613 hrow.setAnchor(element.getPath()); 614 hrow.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); 615 hrow.setLineColor(1); 616 hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); 617 hrow.getCells().add(gen.new Cell(null, null, sName+":All Types", "", null)); 618 switch (context.getStructureMode()) { 619 case BINDINGS: 620 case OBLIGATIONS: 621 for (Column col : columns) { 622 hrow.getCells().add(gen.new Cell()); 623 } 624 break; 625 case SUMMARY: 626 hrow.getCells().add(gen.new Cell()); 627 hrow.getCells().add(gen.new Cell()); 628 hrow.getCells().add(gen.new Cell()); 629 hrow.getCells().add(gen.new Cell(null, null, "Content/Rules for all Types", "", null)); 630 } 631 row.getSubRows().add(hrow); 632 row = hrow; 633 } 634 635 Row currRow = row; 636 List<ElementChoiceGroup> groups = readChoices(element, children); 637 boolean isExtension = Utilities.existsInList(tail(element.getPath()), "extension", "modifierExtension"); 638 if (!element.prohibited()) { 639 for (ElementDefinition child : children) { 640 if (!child.hasSliceName()) { 641 currRow = row; 642 } 643 Row childRow = chooseChildRowByGroup(gen, currRow, groups, child, element, isConstraintMode); 644 645 if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) { 646 currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport, rc, anchorPrefix, srcSD, columns); 647 } 648 } 649 } 650// if (!snapshot && (extensions == null || !extensions)) 651// for (ElementDefinition child : children) 652// if (child.getPath().endsWith(".extension") || child.getPath().endsWith(".modifierExtension")) 653// genElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, true, false, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants); 654 } 655 if (typesRow != null && !element.prohibited() && context.getStructureMode() == StructureDefinitionRendererMode.SUMMARY) { 656 makeChoiceRows(typesRow.getSubRows(), element, gen, corePath, profileBaseFileName, mustSupport, srcSD); 657 } 658 } 659 return slicingRow; 660 } 661 662 private void genElementObligations(HierarchicalTableGenerator gen, ElementDefinition element, List<Column> columns, Row row) throws IOException { 663 for (Column col : columns) { 664 Cell gc = gen.new Cell(); 665 row.getCells().add(gc); 666 List<ElementDefinitionObligationComponent> obligations = collectObligations(element, col.id); 667 if (obligations.size() > 0) { 668 Piece p = gen.new Piece(null); 669 gc.addPiece(p); 670 if (obligations.size() == 1) { 671 renderObligation(p.getChildren(), obligations.get(0)); 672 } else { 673 XhtmlNode ul = p.getChildren().ul(); 674 for (ElementDefinitionObligationComponent ob : obligations) { 675 renderObligation(ul.li().getChildNodes(), ob); 676 } 677 } 678 } 679 680 } 681 } 682 683 private List<ElementDefinitionObligationComponent> collectObligations(ElementDefinition element, String id) { 684 List<ElementDefinitionObligationComponent> res = new ArrayList<>(); 685 for (ElementDefinitionObligationComponent ob : element.getObligation()) { 686 if (("$all".equals(id) && !ob.hasActor()) || (ob.hasActor(id))) { 687 res.add(ob); 688 } 689 } 690 return res; 691 } 692 693 private void renderObligation(XhtmlNodeList children, ElementDefinitionObligationComponent ob) throws IOException { 694 if ("http://hl7.org/fhir/tools/CodeSystem/obligation".equals(ob.getCode().getSystem())) { 695 boolean first = true; 696 String[] codes = ob.getCode().getCode().split("\\+"); 697 for (String code : codes) { 698 if (first) first = false; else children.tx(" & "); 699 int i = code.indexOf(":"); 700 if (i > -1) { 701 String c = code.substring(0, i); 702 code = code.substring(i+1); 703 children.b().tx(c.toUpperCase()); 704 children.tx(":"); 705 } 706 CodeResolution cr = resolveCode("http://hl7.org/fhir/tools/CodeSystem/obligation", code); 707 code = code.replace("will-", "").replace("can-", ""); 708 if (cr.getLink() != null) { 709 children.ah(cr.getLink(), cr.getHint()).tx(code); 710 } else { 711 children.span(null, cr.getHint()).tx(code); 712 } 713 } 714 715 } else { 716 CodeResolution cr = resolveCode(ob.getCode()); 717 if (cr.getLink() != null) { 718 children.ah(cr.getLink(), cr.getHint()).tx(cr.getDisplay()); 719 } else { 720 children.span(null, cr.getHint()).tx(cr.getDisplay()); 721 } 722 } 723 if (ob.hasFilter() || ob.hasUsage()) { 724 children.tx(" ("); 725 boolean ffirst = !ob.hasFilter(); 726 if (ob.hasFilter()) { 727 children.span(null, ob.getFilterDocumentation()).code().tx(ob.getFilter()); 728 } 729 for (UsageContext uc : ob.getUsage()) { 730 if (ffirst) ffirst = false; else children.tx(","); 731 if (!uc.getCode().is("http://terminology.hl7.org/CodeSystem/usage-context-type", "jurisdiction")) { 732 children.tx(displayForUsage(uc.getCode())); 733 children.tx("="); 734 } 735 CodeResolution ccr = resolveCode(uc.getValueCodeableConcept()); 736 children.ah(ccr.getLink(), ccr.getHint()).tx(ccr.getDisplay()); 737 } 738 children.tx(")"); 739 } 740 // usage 741 // filter 742 // process 743 } 744 745 746 private String displayForUsage(Coding c) { 747 if (c.hasDisplay()) { 748 return c.getDisplay(); 749 } 750 if ("http://terminology.hl7.org/CodeSystem/usage-context-type".equals(c.getSystem())) { 751 return c.getCode(); 752 } 753 return c.getCode(); 754 } 755 756 private void genElementBindings(HierarchicalTableGenerator gen, ElementDefinition element, List<Column> columns, Row row, StructureDefinition profile, String corepath) { 757 for (Column col : columns) { 758 Cell gc = gen.new Cell(); 759 row.getCells().add(gc); 760 List<ElementDefinitionBindingAdditionalComponent> bindings = collectBindings(element, col.id); 761 if (bindings.size() > 0) { 762 Piece p = gen.new Piece(null); 763 gc.addPiece(p); 764 new AdditionalBindingsRenderer(context.getPkp(), corepath, profile, element.getPath(), context, null, this).render(p.getChildren(), bindings); 765 } 766 } 767 } 768 769 private List<ElementDefinitionBindingAdditionalComponent> collectBindings(ElementDefinition element, String type) { 770 List<ElementDefinitionBindingAdditionalComponent> res = new ArrayList<>(); 771 if (element.hasBinding()) { 772 ElementDefinitionBindingComponent b = element.getBinding(); 773 if (b.hasStrength() && type.equals(b.getStrength().toCode())) { 774 ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); 775 res.add(ab.setAny(false).setDocumentation(b.getDescription()).setValueSet(b.getValueSet())); 776 } 777 if ("maximum".equals(type) && b.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) { 778 ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); 779 res.add(ab.setAny(false).setValueSet(ToolingExtensions.readStringExtension(b, ToolingExtensions.EXT_MAX_VALUESET))); 780 } 781 if ("minimum".equals(type) && b.hasExtension(ToolingExtensions.EXT_MIN_VALUESET)) { 782 ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); 783 res.add(ab.setAny(false).setValueSet(ToolingExtensions.readStringExtension(b, ToolingExtensions.EXT_MIN_VALUESET))); 784 } 785 for (ElementDefinitionBindingAdditionalComponent t : b.getAdditional()) { 786 if (type.equals(t.getPurpose().toCode())) { 787 res.add(t); 788 } 789 } 790 for (Extension ext : b.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)) { 791 if (type.equals(ext.getExtensionString("purpose"))) { 792 ElementDefinitionBindingAdditionalComponent ab = new ElementDefinitionBindingAdditionalComponent(); 793 if (ext.hasExtension("any")) { 794 ab.setAny(ToolingExtensions.readBooleanExtension(ext, "any")); 795 } 796 if (ext.hasExtension("purpose")) { 797 ab.setPurpose(AdditionalBindingPurposeVS.fromCode(ToolingExtensions.readStringExtension(ext, "purpose"))); 798 } 799 if (ext.hasExtension("documentation")) { 800 ab.setDocumentation(ToolingExtensions.readStringExtension(ext, "documentation")); 801 } 802 if (ext.hasExtension("shortDoco")) { 803 ab.setShortDoco(ToolingExtensions.readStringExtension(ext, "shortDoco")); 804 } 805 if (ToolingExtensions.hasExtension(ext, "usage")) { 806 ab.addUsage(ext.getExtensionByUrl("usage").getValueUsageContext()); 807 } 808 if (ext.hasExtension("valueSet")) { 809 ab.setValueSet(ToolingExtensions.readStringExtension(ext, "valueSet")); 810 } 811 res.add(ab); 812 } 813 } 814 } 815 return res; 816 } 817 818 public Cell genElementNameCell(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath, 819 String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef, 820 boolean ext, UnusedTracker used, String ref, String sName, List<ElementDefinition> elements) throws IOException { 821 String hint = ""; 822 hint = checkAdd(hint, (element.hasSliceName() ? translate("sd.table", "Slice")+" "+element.getSliceName() : "")); 823 if (hasDef && element.hasDefinition()) { 824 hint = checkAdd(hint, (hasDef && element.hasSliceName() ? ": " : "")); 825 hint = checkAdd(hint, !hasDef ? null : gt(element.getDefinitionElement())); 826 } 827 if (element.hasSlicing() && slicesExist(elements, element)) { // some elements set up slicing but don't actually slice, so we don't augment the name 828 sName = "Slices for "+sName; 829 } 830 Cell left = gen.new Cell(null, ref, sName, hint, null); 831 row.getCells().add(left); 832 return left; 833 } 834 835 public List<Cell> genElementCells(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath, 836 String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef, 837 boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport, boolean allowSubRows, RenderingContext rc) throws IOException { 838 List<Cell> res = new ArrayList<>(); 839 Cell gc = gen.new Cell(); 840 row.getCells().add(gc); 841 res.add(gc); 842 if (element != null && element.getIsModifier()) { 843 checkForNoChange(element.getIsModifierElement(), gc.addStyledText(translate("sd.table", "This element is a modifier element"), "?!", null, null, null, false)); 844 } 845 if (element != null && element.getMustSupport()) { 846 checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element must be supported"), "S", "white", "red", null, false)); 847 } 848 if (element != null && element.getIsSummary()) { 849 checkForNoChange(element.getIsSummaryElement(), gc.addStyledText(translate("sd.table", "This element is included in summaries"), "\u03A3", null, null, null, false)); 850 } 851 if (element != null && (hasNonBaseConstraints(element.getConstraint()) || hasNonBaseConditions(element.getCondition()))) { 852 Piece p = gc.addText(CONSTRAINT_CHAR); 853 p.setHint(translate("sd.table", "This element has or is affected by constraints ("+listConstraintsAndConditions(element)+")")); 854 p.addStyle(CONSTRAINT_STYLE); 855 p.setReference(Utilities.pathURL(VersionUtilities.getSpecUrl(context.getWorker().getVersion()), "conformance-rules.html#constraints")); 856 } 857 if (element != null && element.hasExtension(ToolingExtensions.EXT_STANDARDS_STATUS)) { 858 StandardsStatus ss = StandardsStatus.fromCode(element.getExtensionString(ToolingExtensions.EXT_STANDARDS_STATUS)); 859 gc.addStyledText("Standards Status = "+ss.toDisplay(), ss.getAbbrev(), "black", ss.getColor(), context.getWorker().getSpecUrl()+"versions.html#std-process", true); 860 } 861 862 ExtensionContext extDefn = null; 863 if (ext) { 864 if (element != null) { 865 if (element.getType().size() == 1 && element.getType().get(0).hasProfile()) { 866 String eurl = element.getType().get(0).getProfile().get(0).getValue(); 867 extDefn = locateExtension(StructureDefinition.class, eurl); 868 if (extDefn == null) { 869 res.add(genCardinality(gen, element, row, hasDef, used, null)); 870 res.add(addCell(row, gen.new Cell(null, null, "?gen-e1? "+element.getType().get(0).getProfile(), null, null))); 871 res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc)); 872 } else { 873 String name = element.hasSliceName() ? element.getSliceName() : urltail(eurl); 874 nameCell.getPieces().get(0).setText(name); 875 // left.getPieces().get(0).setReference((String) extDefn.getExtensionStructure().getTag("filename")); 876 nameCell.getPieces().get(0).setHint(translate("sd.table", "Extension URL")+" = "+extDefn.getUrl()); 877 res.add(genCardinality(gen, element, row, hasDef, used, extDefn.getElement())); 878 ElementDefinition valueDefn = extDefn.getExtensionValueDefinition(); 879 if (valueDefn != null && !"0".equals(valueDefn.getMax())) 880 res.add(genTypes(gen, row, valueDefn, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); 881 else // if it's complex, we just call it nothing 882 // genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile); 883 res.add(addCell(row, gen.new Cell(null, null, "("+translate("sd.table", "Complex")+")", null, null))); 884 res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport, allowSubRows, rc)); 885 } 886 } else { 887 res.add(genCardinality(gen, element, row, hasDef, used, null)); 888 if ("0".equals(element.getMax())) 889 res.add(addCell(row, gen.new Cell())); 890 else 891 res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); 892 res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc)); 893 } 894 } 895 } else if (element != null) { 896 res.add(genCardinality(gen, element, row, hasDef, used, null)); 897 if (hasDef && !"0".equals(element.getMax()) && typesRow == null) 898 res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); 899 else 900 res.add(addCell(row, gen.new Cell())); 901 res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows, rc)); 902 } 903 return res; 904 } 905 906 private Cell genCardinality(HierarchicalTableGenerator gen, ElementDefinition definition, Row row, boolean hasDef, UnusedTracker tracker, ElementDefinition fallback) { 907 IntegerType min = !hasDef ? new IntegerType() : definition.hasMinElement() ? definition.getMinElement() : new IntegerType(); 908 StringType max = !hasDef ? new StringType() : definition.hasMaxElement() ? definition.getMaxElement() : new StringType(); 909 if (min.isEmpty() && definition.getUserData(ProfileUtilities.UD_DERIVATION_POINTER) != null) { 910 ElementDefinition base = (ElementDefinition) definition.getUserData(ProfileUtilities.UD_DERIVATION_POINTER); 911 if (base.hasMinElement()) { 912 min = base.getMinElement().copy(); 913 min.setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, true); 914 } 915 } 916 if (max.isEmpty() && definition.getUserData(ProfileUtilities.UD_DERIVATION_POINTER) != null) { 917 ElementDefinition base = (ElementDefinition) definition.getUserData(ProfileUtilities.UD_DERIVATION_POINTER); 918 if (base.hasMaxElement()) { 919 max = base.getMaxElement().copy(); 920 max.setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, true); 921 } 922 } 923 if (min.isEmpty() && fallback != null) 924 min = fallback.getMinElement(); 925 if (max.isEmpty() && fallback != null) 926 max = fallback.getMaxElement(); 927 928 if (!max.isEmpty()) 929 tracker.used = !max.getValue().equals("0"); 930 931 String hint = null; 932 if (max.hasValue() && min.hasValue() && "*".equals(max.getValue()) && 0 == min.getValue()) { 933 if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { 934 String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); 935 if ("present".equals(code)) { 936 hint = "This element is present as a JSON Array even when there are no items in the instance"; 937 } else { 938 hint = "This element may be present as a JSON Array even when there are no items in the instance"; 939 } 940 } 941 } 942 Cell cell = gen.new Cell(null, null, null, null, null); 943 row.getCells().add(cell); 944 if (!min.isEmpty() || !max.isEmpty()) { 945 cell.addPiece(checkForNoChange(min, gen.new Piece(null, !min.hasValue() ? "" : Integer.toString(min.getValue()), hint))); 946 cell.addPiece(checkForNoChange(min, max, gen.new Piece(null, "..", hint))); 947 cell.addPiece(checkForNoChange(max, gen.new Piece(null, !max.hasValue() ? "" : max.getValue(), hint))); 948 } 949 return cell; 950 } 951 952 public List<ElementDefinition> supplementMissingDiffElements(StructureDefinition profile) { 953 List<ElementDefinition> list = new ArrayList<>(); 954 list.addAll(profile.getDifferential().getElement()); 955 if (list.isEmpty()) { 956 ElementDefinition root = new ElementDefinition().setPath(profile.getTypeName()); 957 root.setId(profile.getTypeName()); 958 list.add(root); 959 } else { 960 if (list.get(0).getPath().contains(".")) { 961 ElementDefinition root = new ElementDefinition().setPath(profile.getTypeName()); 962 root.setId(profile.getTypeName()); 963 list.add(0, root); 964 } 965 } 966 insertMissingSparseElements(list); 967 return list; 968 } 969 970 private boolean usesMustSupport(List<ElementDefinition> list) { 971 for (ElementDefinition ed : list) 972 if (ed.hasMustSupport() && ed.getMustSupport()) 973 return true; 974 return false; 975 } 976 977 978 979 private Row chooseChildRowByGroup(HierarchicalTableGenerator gen, Row row, List<ElementChoiceGroup> groups, ElementDefinition element, ElementDefinition parent, boolean isConstraintMode) { 980 String name = tail(element.getPath()); 981 for (ElementChoiceGroup grp : groups) { 982 if (grp.getElements().contains(name)) { 983 if (grp.getRow() == null) { 984 grp.setRow(makeChoiceElementRow(gen, row, grp, parent, isConstraintMode)); 985 } 986 return grp.getRow(); 987 } 988 } 989 return row; 990 } 991 992 private Row makeChoiceElementRow(HierarchicalTableGenerator gen, Row prow, ElementChoiceGroup grp, ElementDefinition parent, boolean isConstraintMode) { 993 if (context.getStructureMode() != StructureDefinitionRendererMode.SUMMARY) { 994 return prow; 995 } 996 Row row = gen.new Row(); 997 row.setAnchor(parent.getPath()+"-"+grp.getName()); 998 row.setColor(context.getProfileUtilities().getRowColor(parent, isConstraintMode)); 999 row.setLineColor(1); 1000 row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); 1001 row.getCells().add(gen.new Cell(null, null, "(Choice of one)", "", null)); 1002 row.getCells().add(gen.new Cell()); 1003 row.getCells().add(gen.new Cell(null, null, (grp.isMandatory() ? "1" : "0")+"..1", "", null)); 1004 row.getCells().add(gen.new Cell()); 1005 row.getCells().add(gen.new Cell()); 1006 prow.getSubRows().add(row); 1007 return row; 1008 } 1009 1010 1011 private void insertMissingSparseElements(List<ElementDefinition> list) { 1012 int i = 1; 1013 while (i < list.size()) { 1014 String[] pathCurrent = list.get(i).getPath().split("\\."); 1015 String[] pathLast = list.get(i-1).getPath().split("\\."); 1016 int firstDiff = 0; // the first entry must be a match 1017 while (firstDiff < pathCurrent.length && firstDiff < pathLast.length && pathCurrent[firstDiff].equals(pathLast[firstDiff])) { 1018 firstDiff++; 1019 } 1020 if (!(isSibling(pathCurrent, pathLast, firstDiff) || isChild(pathCurrent, pathLast, firstDiff))) { 1021 // now work backwards down to lastMatch inserting missing path nodes 1022 ElementDefinition parent = findParent(list, i, list.get(i).getPath()); 1023 int parentDepth = Utilities.charCount(parent.getPath(), '.')+1; 1024 int childDepth = Utilities.charCount(list.get(i).getPath(), '.')+1; 1025 if (childDepth > parentDepth + 1) { 1026 String basePath = parent.getPath(); 1027 String baseId = parent.getId(); 1028 for (int index = parentDepth; index >= firstDiff; index--) { 1029 String mtail = makeTail(pathCurrent, parentDepth, index); 1030 ElementDefinition root = new ElementDefinition().setPath(basePath+"."+mtail); 1031 root.setId(baseId+"."+mtail); 1032 list.add(i, root); 1033 } 1034 } 1035 } 1036 i++; 1037 } 1038 } 1039 1040 1041 private String urltail(String path) { 1042 if (path.contains("#")) 1043 return path.substring(path.lastIndexOf('#')+1); 1044 if (path.contains("/")) 1045 return path.substring(path.lastIndexOf('/')+1); 1046 else 1047 return path; 1048 1049 } 1050 1051 private boolean standardExtensionSlicing(ElementDefinition element) { 1052 String t = tail(element.getPath()); 1053 return (t.equals("extension") || t.equals("modifierExtension")) 1054 && 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); 1055 } 1056 1057 public Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException { 1058 return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows, rc); 1059 } 1060 1061 public Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException { 1062 Cell c = gen.new Cell(); 1063 row.getCells().add(c); 1064 1065 if (used) { 1066 if (logicalModel && ToolingExtensions.hasExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) { 1067 if (root) { 1068 c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); 1069 c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"), null)); 1070 } else if (!root && ToolingExtensions.hasExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace") && 1071 !ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace").equals(ToolingExtensions.readStringExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))) { 1072 c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); 1073 c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"), null)); 1074 } 1075 } 1076 if (root) { 1077 if (profile != null && profile.getAbstract()) { 1078 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1079 c.addPiece(gen.new Piece(null, "This is an abstract "+(profile.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profile" : "type")+". ", null)); 1080 1081 List<StructureDefinition> children = new ArrayList<>(); 1082 for (StructureDefinition sd : context.getWorker().fetchResourcesByType(StructureDefinition.class)) { 1083 if (sd.hasBaseDefinition() && sd.getBaseDefinition().equals(profile.getUrl())) { 1084 children.add(sd); 1085 } 1086 } 1087 if (!children.isEmpty()) { 1088 c.addPiece(gen.new Piece(null, "Child "+(profile.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profiles" : "types")+": ", null)); 1089 boolean first = true; 1090 for (StructureDefinition sd : children) { 1091 if (first) first = false; else c.addPiece(gen.new Piece(null, ", ", null)); 1092 c.addPiece(gen.new Piece(sd.getUserString("path"), sd.getTypeName(), null)); 1093 } 1094 } 1095 } 1096 } 1097 if (definition.getPath().endsWith("url") && definition.hasFixed()) { 1098 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen"))); 1099 } else { 1100 if (definition != null && definition.hasShort()) { 1101 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1102 c.addPiece(checkForNoChange(definition.getShortElement(), gen.new Piece(null, gt(definition.getShortElement()), null))); 1103 } else if (fallback != null && fallback.hasShort()) { 1104 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1105 c.addPiece(gen.new Piece(null, gt(fallback.getShortElement()), null).addStyle("opacity: 0.5")); 1106 } 1107 if (url != null) { 1108 if (!c.getPieces().isEmpty()) 1109 c.addPiece(gen.new Piece("br")); 1110 String fullUrl = url.startsWith("#") ? baseURL+url : url; 1111 StructureDefinition ed = context.getWorker().fetchResource(StructureDefinition.class, url, profile); 1112 String ref = null; 1113 String ref2 = null; 1114 String fixedUrl = null; 1115 if (ed != null) { 1116 String p = ed.getUserString("path"); 1117 if (p != null) { 1118 ref = p.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p : Utilities.pathURL(corePath, p); 1119 } 1120 fixedUrl = getFixedUrl(ed); 1121 if (fixedUrl != null) {// if its null, we guess that it's not a profiled extension? 1122 if (fixedUrl.equals(url)) 1123 fixedUrl = null; 1124 else { 1125 StructureDefinition ed2 = context.getWorker().fetchResource(StructureDefinition.class, fixedUrl); 1126 if (ed2 != null) { 1127 String p2 = ed2.getUserString("path"); 1128 if (p2 != null) { 1129 ref2 = p2.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p2 : Utilities.pathURL(corePath, p2); 1130 } 1131 } 1132 } 1133 } 1134 } 1135 if (fixedUrl == null) { 1136 if (!Utilities.noString(fullUrl)) { 1137 c.getPieces().add(gen.new Piece(null, translate("sd.table", "URL")+": ", null).addStyle("font-weight:bold")); 1138 c.getPieces().add(gen.new Piece(ref, fullUrl, null)); 1139 } 1140 } else { 1141 // reference to a profile take on the extension show the base URL 1142 c.getPieces().add(gen.new Piece(null, translate("sd.table", "URL")+": ", null).addStyle("font-weight:bold")); 1143 c.getPieces().add(gen.new Piece(ref2, fixedUrl, null)); 1144 c.getPieces().add(gen.new Piece(null, translate("sd.table", " profiled by ")+" ", null).addStyle("font-weight:bold")); 1145 c.getPieces().add(gen.new Piece(ref, fullUrl, null)); 1146 1147 } 1148 } 1149 1150 if (definition.hasSlicing()) { 1151 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1152 c.getPieces().add(gen.new Piece(null, translate("sd.table", "Slice")+": ", null).addStyle("font-weight:bold")); 1153 c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); 1154 } 1155 if (!definition.getPath().contains(".") && ToolingExtensions.hasExtension(profile, ToolingExtensions.EXT_BINDING_STYLE)) { 1156 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1157 c.getPieces().add(gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold")); 1158 c.getPieces().add(gen.new Piece(null, "This type can be bound to a value set using the ", null)); 1159 c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_BINDING_STYLE), null)); 1160 c.getPieces().add(gen.new Piece(null, " binding style", null)); 1161 1162 } 1163 if (definition.hasExtension(ToolingExtensions.EXT_IMPLIED_PREFIX)) { 1164 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1165 c.getPieces().add(gen.new Piece(null, "When this element is read ", null)); 1166 Piece piece = gen.new Piece("code"); 1167 piece.addHtml(new XhtmlNode(NodeType.Text).setContent(ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_IMPLIED_PREFIX))); 1168 c.getPieces().add(piece); 1169 c.getPieces().add(gen.new Piece(null, " is prefixed to the value before validation", null)); 1170 } 1171 1172 if (definition.hasExtension(ToolingExtensions.EXT_EXTENSION_STYLE)) { 1173 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1174 String es = definition.getExtensionString(ToolingExtensions.EXT_EXTENSION_STYLE); 1175 if ("named-elements".equals(es)) { 1176 if (rc.hasLink(KnownLinkType.JSON_NAMES)) { 1177 c.getPieces().add(gen.new Piece(rc.getLink(KnownLinkType.JSON_NAMES), "This element can be extended by named JSON elements", null)); 1178 } else { 1179 c.getPieces().add(gen.new Piece(ToolingExtensions.WEB_EXTENSION_STYLE, "This element can be extended by named JSON elements", null)); 1180 } 1181 } 1182 } 1183 if (definition.hasExtension(ToolingExtensions.EXT_ID_EXPECTATION)) { 1184 String ide = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_ID_EXPECTATION); 1185 if (ide.equals("optional")) { 1186 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1187 c.getPieces().add(gen.new Piece(null, "Id may or not be present (this is the default for elements but not resources)", null)); 1188 } else if (ide.equals("required")) { 1189 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1190 c.getPieces().add(gen.new Piece(null, "Id is required to be present (this is the default for resources but not elements)", null)); 1191 } else if (ide.equals("required")) { 1192 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1193 c.getPieces().add(gen.new Piece(null, "An ID is not allowed in this context", null)); 1194 } 1195 } 1196 if (definition.hasExtension(ToolingExtensions.EXT_XML_NAME)) { 1197 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1198 if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { 1199 c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML")+": ", null).addStyle("font-weight:bold")); 1200 c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null)); 1201 c.getPieces().add(gen.new Piece(null, " (", null)); 1202 c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); 1203 c.getPieces().add(gen.new Piece(null, ")", null)); 1204 } else { 1205 c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Element Name")+": ", null).addStyle("font-weight:bold")); 1206 c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null)); 1207 } 1208 } else if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { 1209 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1210 c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); 1211 c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); 1212 } 1213 if (definition.hasExtension(ToolingExtensions.EXT_JSON_EMPTY)) { 1214 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1215 String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); 1216 if ("present".equals(code)) { 1217 c.getPieces().add(gen.new Piece(null, "JSON: This element is present as a JSON Array even when there are no items in the instance", null)); 1218 } else { 1219 c.getPieces().add(gen.new Piece(null, "JSON: This element may be present as a JSON Array even when there are no items in the instance", null)); 1220 } 1221 } 1222 String jn = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_NAME); 1223 if (!Utilities.noString(jn)) { 1224 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1225 if (definition.getPath().contains(".")) { 1226 c.getPieces().add(gen.new Piece(null, translate("sd.table", "JSON Property Name")+": ", null).addStyle("font-weight:bold")); 1227 c.getPieces().add(gen.new Piece(null, jn, null)); 1228 } else { 1229 c.getPieces().add(gen.new Piece(null, translate("sd.table", "JSON Property Name for Type")+": ", null).addStyle("font-weight:bold")); 1230 Piece piece = gen.new Piece("code"); 1231 piece.addHtml(new XhtmlNode(NodeType.Text).setContent(jn)); 1232 c.getPieces().add(piece); 1233 } 1234 } 1235 1236 if (ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE)) { 1237 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1238 c.getPieces().add(gen.new Piece(null, "JSON: The type of this element is inferred from the JSON type in the instance", null)); 1239 } 1240 if (ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_NULLABLE)) { 1241 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1242 c.getPieces().add(gen.new Piece(null, "JSON: This object can be represented as null in the JSON structure (which counts as 'present' for cardinality purposes)", null)); 1243 } 1244 if (definition.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { 1245 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1246 String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_EMPTY); 1247 c.getPieces().add(gen.new Piece(null, "JSON: Represented as a single JSON Object with named properties using the value of the "+code+" child as the key", null)); 1248 } 1249 if (definition.hasExtension(ToolingExtensions.EXT_TYPE_SPEC)) { 1250 for (Extension e : definition.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { 1251 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1252 String cond = ToolingExtensions.readStringExtension(e, "condition"); 1253 String type = ToolingExtensions.readStringExtension(e, "type"); 1254 c.getPieces().add(gen.new Piece(null, "JSON: If ", null)); 1255 Piece piece = gen.new Piece("code"); 1256 piece.addHtml(new XhtmlNode(NodeType.Text).setContent(cond)); 1257 c.getPieces().add(piece); 1258 c.getPieces().add(gen.new Piece(null, "then the type is ", null)); 1259 StructureDefinition sd = context.getWorker().fetchTypeDefinition(type); 1260 if (sd == null) { 1261 c.getPieces().add(gen.new Piece("<code>")); 1262 c.getPieces().add(gen.new Piece(null, type, null)); 1263 c.getPieces().add(gen.new Piece("</code>")); 1264 } else { 1265 c.getPieces().add(gen.new Piece(sd.getUserString("path"), sd.getTypeName(), null)); 1266 } 1267 } 1268 } 1269 if (definition != null) { 1270 ElementDefinitionBindingComponent binding = null; 1271 if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty()) 1272 binding = makeUnifiedBinding(valueDefn.getBinding(), valueDefn); 1273 else if (definition.hasBinding()) 1274 binding = makeUnifiedBinding(definition.getBinding(), definition); 1275 if (binding!=null && !binding.isEmpty()) { 1276 if (!c.getPieces().isEmpty()) 1277 c.addPiece(gen.new Piece("br")); 1278 BindingResolution br = context.getPkp() == null ? makeNullBr(binding) : context.getPkp().resolveBinding(profile, binding, definition.getPath()); 1279 c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold"))); 1280 c.getPieces().add(checkForNoChange(binding.getValueSetElement(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, null))); 1281 if (binding.hasStrength()) { 1282 c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, " (", null))); 1283 c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), egt(binding.getStrengthElement()), binding.getStrength().getDefinition()))); 1284 c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, ")", null))); 1285 } 1286 if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) { 1287 c.getPieces().add(gen.new Piece(null, ": ", null)); 1288 c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()))); 1289 } 1290 1291 AdditionalBindingsRenderer abr = new AdditionalBindingsRenderer(context.getPkp(), corePath, profile, definition.getPath(), rc, null, this); 1292 if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) { 1293 abr.seeMaxBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MAX_VALUESET)); 1294 } 1295 if (binding.hasExtension(ToolingExtensions.EXT_MIN_VALUESET)) { 1296 abr.seeMinBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MIN_VALUESET)); 1297 } 1298 if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) { 1299 abr.seeAdditionalBindings(binding.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)); 1300 } 1301 abr.render(gen, c); 1302 } 1303 for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { 1304 if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) { 1305 if (!c.getPieces().isEmpty()) 1306 c.addPiece(gen.new Piece("br")); 1307 c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold"))); 1308 c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, gt(inv.getHumanElement()), null))); 1309 } 1310 } 1311 if ((definition.hasBase() && "*".equals(definition.getBase().getMax())) || (definition.hasMax() && "*".equals(definition.getMax()))) { 1312 if (c.getPieces().size() > 0) 1313 c.addPiece(gen.new Piece("br")); 1314 if (definition.hasOrderMeaning()) { 1315 c.getPieces().add(gen.new Piece(null, "This repeating element order: "+definition.getOrderMeaning(), null)); 1316 } else { 1317 // don't show this, this it's important: c.getPieces().add(gen.new Piece(null, "This repeating element has no defined order", null)); 1318 } 1319 } 1320 if (definition.hasFixed()) { 1321 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1322 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value")+": ", null).addStyle("font-weight:bold"))); 1323 if (!useTableForFixedValues || !allowSubRows || definition.getFixed().isPrimitive()) { 1324 String s = buildJson(definition.getFixed()); 1325 String link = null; 1326 if (Utilities.isAbsoluteUrl(s) && context.getPkp() != null) 1327 link = context.getPkp().getLinkForUrl(corePath, s); 1328 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); 1329 } else { 1330 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "As shown", null).addStyle("color: darkgreen"))); 1331 genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false); 1332 } 1333 if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) { 1334 Piece p = describeCoded(gen, definition.getFixed()); 1335 if (p != null) 1336 c.getPieces().add(p); 1337 } 1338 } else if (definition.hasPattern()) { 1339 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1340 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, translate("sd.table", "Required Pattern")+": ", null).addStyle("font-weight:bold"))); 1341 if (!useTableForFixedValues || !allowSubRows || definition.getPattern().isPrimitive()) 1342 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); 1343 else { 1344 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "At least the following", null).addStyle("color: darkgreen"))); 1345 genFixedValue(gen, row, definition.getPattern(), snapshot, true, corePath, mustSupportOnly); 1346 } 1347 } else if (definition.hasExample()) { 1348 for (ElementDefinitionExampleComponent ex : definition.getExample()) { 1349 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1350 c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, translate("sd.table", "Example")+("".equals("General")? "" : " "+ex.getLabel())+": ", null).addStyle("font-weight:bold"))); 1351 c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); 1352 } 1353 } 1354 if (definition.hasMaxLength() && definition.getMaxLength()!=0) { 1355 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 1356 c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold"))); 1357 c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); 1358 } 1359 if (profile != null) { 1360 for (StructureDefinitionMappingComponent md : profile.getMapping()) { 1361 if (md.hasExtension(ToolingExtensions.EXT_TABLE_NAME)) { 1362 ElementDefinitionMappingComponent map = null; 1363 for (ElementDefinitionMappingComponent m : definition.getMapping()) 1364 if (m.getIdentity().equals(md.getIdentity())) 1365 map = m; 1366 if (map != null) { 1367 for (int i = 0; i<definition.getMapping().size(); i++){ 1368 c.addPiece(gen.new Piece("br")); 1369 c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(md, ToolingExtensions.EXT_TABLE_NAME)+": " + map.getMap(), null)); 1370 } 1371 } 1372 } 1373 } 1374 } 1375 } 1376 } 1377 } 1378 return c; 1379 } 1380 1381 1382 private Piece checkForNoChange(Element source, Piece piece) { 1383 if (source.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS)) { 1384 piece.addStyle("opacity: 0.5"); 1385 } 1386 return piece; 1387 } 1388 1389 private String checkForNoChange(Element source) { 1390 if (source.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS)) { 1391 return "opacity: 0.5"; 1392 } else { 1393 return null; 1394 } 1395 } 1396 1397 private Cell genTypes(HierarchicalTableGenerator gen, Row r, ElementDefinition e, String profileBaseFileName, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean mustSupportMode) { 1398 Cell c = gen.new Cell(); 1399 r.getCells().add(c); 1400 if (e.hasContentReference()) { 1401 ElementInStructure ed = getElementByName(profile.getSnapshot().getElement(), e.getContentReference(), profile); 1402 if (ed == null) 1403 c.getPieces().add(gen.new Piece(null, translate("sd.table", "Unknown reference to %s", e.getContentReference()), null)); 1404 else { 1405 if (ed.getSource() == profile) { 1406 c.getPieces().add(gen.new Piece(null, translate("sd.table", "See ", ed.getElement().getPath()), null)); 1407 c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), tail(ed.getElement().getPath()), ed.getElement().getPath())); 1408 } else { 1409 c.getPieces().add(gen.new Piece(null, translate("sd.table", "See ", ed.getElement().getPath()), null)); 1410 c.getPieces().add(gen.new Piece(pfx(corePath, ed.getSource().getUserString("path"))+"#"+ed.getElement().getPath(), tail(ed.getElement().getPath())+" ("+ed.getSource().getTypeName()+")", ed.getElement().getPath())); 1411 } 1412 } 1413 return c; 1414 } 1415 List<TypeRefComponent> types = e.getType(); 1416 if (!e.hasType()) { 1417 if (root) { // we'll use base instead of types then 1418 StructureDefinition bsd = profile == null ? null : context.getWorker().fetchResource(StructureDefinition.class, profile.getBaseDefinition(), profile); 1419 if (bsd != null) { 1420 if (bsd.hasUserData("path")) { 1421 c.getPieces().add(gen.new Piece(Utilities.isAbsoluteUrl(bsd.getUserString("path")) ? bsd.getUserString("path") : imagePath +bsd.getUserString("path"), bsd.getName(), null)); 1422 } else { 1423 c.getPieces().add(gen.new Piece(null, bsd.getName(), null)); 1424 } 1425 } 1426 return c; 1427 } else if (e.hasContentReference()) { 1428 return c; 1429 } else { 1430 ElementDefinition d = (ElementDefinition) e.getUserData(ProfileUtilities.UD_DERIVATION_POINTER); 1431 if (d != null && d.hasType()) { 1432 types = new ArrayList<ElementDefinition.TypeRefComponent>(); 1433 for (TypeRefComponent tr : d.getType()) { 1434 TypeRefComponent tt = tr.copy(); 1435 tt.setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, true); 1436 types.add(tt); 1437 } 1438 } else { 1439 return c; 1440 } 1441 } 1442 } 1443 1444 boolean first = true; 1445 1446 TypeRefComponent tl = null; 1447 for (TypeRefComponent t : types) { 1448 if (!mustSupportMode || allTypesMustSupport(e) || isMustSupport(t)) { 1449 if (first) { 1450 first = false; 1451 } else { 1452 c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); 1453 } 1454 tl = t; 1455 if (t.hasTarget()) { 1456 c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null)); 1457 if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { 1458 c.addPiece(gen.new Piece(null, " ", null)); 1459 c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); 1460 } 1461 c.getPieces().add(gen.new Piece(null, "(", null)); 1462 boolean tfirst = true; 1463 for (CanonicalType u : t.getTargetProfile()) { 1464 if (!mustSupportMode || allProfilesMustSupport(t.getTargetProfile()) || isMustSupport(u)) { 1465 if (tfirst) 1466 tfirst = false; 1467 else 1468 c.addPiece(gen.new Piece(null, " | ", null)); 1469 genTargetLink(gen, profileBaseFileName, corePath, c, t, u.getValue(), null); 1470 if (!mustSupportMode && isMustSupport(u) && e.getMustSupport()) { 1471 c.addPiece(gen.new Piece(null, " ", null)); 1472 c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); 1473 } 1474 } 1475 } 1476 c.getPieces().add(gen.new Piece(null, ")", null)); 1477 if (t.getAggregation().size() > 0) { 1478 c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", " {", null)); 1479 boolean firstA = true; 1480 for (Enumeration<AggregationMode> a : t.getAggregation()) { 1481 if (firstA == true) 1482 firstA = false; 1483 else 1484 c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", ", ", null)); 1485 c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", codeForAggregation(a.getValue()), hintForAggregation(a.getValue()))); 1486 } 1487 c.getPieces().add(gen.new Piece(corePath+"valueset-resource-aggregation-mode.html", "}", null)); 1488 } 1489 } else if (t.hasProfile() && (!t.getWorkingCode().equals("Extension") || isProfiledType(t.getProfile()))) { // a profiled type 1490 String ref; 1491 boolean pfirst = true; 1492 for (CanonicalType p : t.getProfile()) { 1493 if (!mustSupportMode || allProfilesMustSupport(t.getProfile()) || isMustSupport(p)) { 1494 if (pfirst) { 1495 pfirst = false; 1496 } else { 1497 c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); 1498 } 1499 1500 ref = context.getPkp() == null ? null : context.getPkp().getLinkForProfile(profile, p.getValue()); 1501 if (ref != null) { 1502 String[] parts = ref.split("\\|"); 1503 if (parts[0].startsWith("http:") || parts[0].startsWith("https:")) { 1504 if (p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT)) { 1505 String pp = p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT); 1506 pp = pp.substring(pp.indexOf(".")); 1507 c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1]+pp, t.getWorkingCode()))); 1508 } else { 1509 c.addPiece(checkForNoChange(t, gen.new Piece(parts[0], parts[1], t.getWorkingCode()))); 1510 } 1511 } else { 1512 c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath+"StructureDefinition")? corePath: "")+parts[0], parts[1], t.getWorkingCode()))); 1513 } 1514 } else { 1515 c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath)? corePath: "")+ref, t.getWorkingCode(), null))); 1516 } 1517 if (!mustSupportMode && isMustSupport(p) && e.getMustSupport()) { 1518 c.addPiece(gen.new Piece(null, " ", null)); 1519 c.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); 1520 } 1521 } 1522 } 1523 } else { 1524 String tc = t.getWorkingCode(); 1525 if (Utilities.isAbsoluteUrl(tc)) { 1526 StructureDefinition sd = context.getWorker().fetchTypeDefinition(tc); 1527 if (sd == null) { 1528 c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, tc), tc, null))); 1529 } else { 1530 c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, tc), sd.getTypeName(), null))); 1531 } 1532 } else if (context.getPkp() != null && context.getPkp().hasLinkFor(tc)) { 1533 c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, tc), tc, null))); 1534 } else { 1535 c.addPiece(checkForNoChange(t, gen.new Piece(null, tc, null))); 1536 } 1537 if (!mustSupportMode && isMustSupportDirect(t) && e.getMustSupport()) { 1538 c.addPiece(gen.new Piece(null, " ", null)); 1539 c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); 1540 } 1541 } 1542 } 1543 } 1544 return c; 1545 } 1546 1547 1548 private String pfx(String prefix, String url) { 1549 return Utilities.isAbsoluteUrl(url) ? url : prefix + url; 1550 } 1551 1552 private void genTargetLink(HierarchicalTableGenerator gen, String profileBaseFileName, String corePath, Cell c, TypeRefComponent t, String u, Resource src) { 1553 if (u.startsWith("http://hl7.org/fhir/StructureDefinition/")) { 1554 StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, u, src); 1555 if (sd != null) { 1556 String disp = sd.hasTitle() ? sd.getTitle() : sd.getName(); 1557 c.addPiece(checkForNoChange(t, gen.new Piece(checkPrepend(corePath, sd.getUserString("path")), disp, null))); 1558 } else { 1559 String rn = u.substring(40); 1560 c.addPiece(checkForNoChange(t, gen.new Piece(context.getPkp().getLinkFor(corePath, rn), rn, null))); 1561 } 1562 } else if (Utilities.isAbsoluteUrl(u)) { 1563 StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, u, src); 1564 if (sd != null && context.getPkp() != null) { 1565 String disp = sd.hasTitle() ? sd.getTitle() : sd.getName(); 1566 String ref = context.getPkp().getLinkForProfile(null, sd.getUrl()); 1567 if (ref != null && ref.contains("|")) 1568 ref = ref.substring(0, ref.indexOf("|")); 1569 c.addPiece(checkForNoChange(t, gen.new Piece(ref, disp, null))); 1570 } else 1571 c.addPiece(checkForNoChange(t, gen.new Piece(null, u, null))); 1572 } else if (t.hasTargetProfile() && u.startsWith("#")) 1573 c.addPiece(checkForNoChange(t, gen.new Piece(corePath+profileBaseFileName+"."+u.substring(1).toLowerCase()+".html", u, null))); 1574 } 1575 1576 private boolean isProfiledType(List<CanonicalType> theProfile) { 1577 for (CanonicalType next : theProfile){ 1578 if (StringUtils.defaultString(next.getValueAsString()).contains(":")) { 1579 return true; 1580 } 1581 } 1582 return false; 1583 } 1584 1585 1586 public String codeForAggregation(AggregationMode a) { 1587 switch (a) { 1588 case BUNDLED : return "b"; 1589 case CONTAINED : return "c"; 1590 case REFERENCED: return "r"; 1591 default: return "?"; 1592 } 1593 } 1594 1595 public String hintForAggregation(AggregationMode a) { 1596 if (a != null) 1597 return a.getDefinition(); 1598 else 1599 return null; 1600 } 1601 1602 1603 private String checkPrepend(String corePath, String path) { 1604 if (context.getPkp() != null && context.getPkp().prependLinks() && !(path.startsWith("http:") || path.startsWith("https:"))) 1605 return corePath+path; 1606 else 1607 return path; 1608 } 1609 1610 1611 private ElementDefinition findParent(List<ElementDefinition> list, int i, String path) { 1612 while (i > 0 && !path.startsWith(list.get(i).getPath()+".")) { 1613 i--; 1614 } 1615 return list.get(i); 1616 } 1617 1618 private boolean isSibling(String[] pathCurrent, String[] pathLast, int firstDiff) { 1619 return pathCurrent.length == pathLast.length && firstDiff == pathCurrent.length-1; 1620 } 1621 1622 1623 private boolean isChild(String[] pathCurrent, String[] pathLast, int firstDiff) { 1624 return pathCurrent.length == pathLast.length+1 && firstDiff == pathLast.length; 1625 } 1626 1627 private String makeTail(String[] pathCurrent, int start, int index) { 1628 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("."); 1629 for (int i = start; i <= index; i++) { 1630 b.append(pathCurrent[i]); 1631 } 1632 return b.toString(); 1633 } 1634 1635 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 { 1636 StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); 1637 String s = tail(element.getPath()); 1638 List<ElementDefinition> children = getChildren(all, element); 1639 boolean isExtension = (s.equals("extension") || s.equals("modifierExtension")); 1640 1641 if (!onlyInformationIsMapping(all, element)) { 1642 Row row = gen.new Row(); 1643 row.setAnchor(element.getPath()); 1644 row.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); 1645 if (element.hasSlicing()) 1646 row.setLineColor(1); 1647 else if (element.hasSliceName()) 1648 row.setLineColor(2); 1649 else 1650 row.setLineColor(0); 1651 boolean hasDef = element != null; 1652 String ref = defPath == null ? null : defPath + element.getId(); 1653 UnusedTracker used = new UnusedTracker(); 1654 used.used = true; 1655 Cell left = gen.new Cell(); 1656 if (element.getType().size() == 1 && element.getType().get(0).isPrimitive()) 1657 left.getPieces().add(gen.new Piece(ref, "\u00A0\u00A0" + s, !hasDef ? null : gt(element.getDefinitionElement())).addStyle("font-weight:bold")); 1658 else 1659 left.getPieces().add(gen.new Piece(ref, "\u00A0\u00A0" + s, !hasDef ? null : gt(element.getDefinitionElement()))); 1660 if (element.hasSliceName()) { 1661 left.getPieces().add(gen.new Piece("br")); 1662 String indent = StringUtils.repeat('\u00A0', 1+2*(element.getPath().split("\\.").length)); 1663 left.getPieces().add(gen.new Piece(null, indent + "("+element.getSliceName() + ")", null)); 1664 } 1665 row.getCells().add(left); 1666 1667 genCardinality(gen, element, row, hasDef, used, null); 1668 if (hasDef && !"0".equals(element.getMax())) 1669 genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, false); 1670 else 1671 row.getCells().add(gen.new Cell()); 1672 generateGridDescription(gen, row, element, null, used.used, null, null, profile, corePath, imagePath, root, null); 1673/* if (element.hasSlicing()) { 1674 if (standardExtensionSlicing(element)) { 1675 used.used = element.hasType() && element.getType().get(0).hasProfile(); 1676 showMissing = false; 1677 } else { 1678 row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); 1679 row.getCells().get(2).getPieces().clear(); 1680 for (Cell cell : row.getCells()) 1681 for (Piece p : cell.getPieces()) { 1682 p.addStyle("font-style: italic"); 1683 } 1684 } 1685 }*/ 1686 rows.add(row); 1687 for (ElementDefinition child : children) 1688 if (child.getMustSupport()) 1689 genGridElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, corePath, imagePath, false, isConstraintMode); 1690 } 1691 } 1692 1693 1694 private ExtensionContext locateExtension(Class<StructureDefinition> class1, String value) { 1695 if (value.contains("#")) { 1696 StructureDefinition ext = context.getWorker().fetchResource(StructureDefinition.class, value.substring(0, value.indexOf("#"))); 1697 if (ext == null) 1698 return null; 1699 String tail = value.substring(value.indexOf("#")+1); 1700 ElementDefinition ed = null; 1701 for (ElementDefinition ted : ext.getSnapshot().getElement()) { 1702 if (tail.equals(ted.getSliceName())) { 1703 ed = ted; 1704 return new ExtensionContext(ext, ed); 1705 } 1706 } 1707 return null; 1708 } else { 1709 StructureDefinition ext = context.getWorker().fetchResource(StructureDefinition.class, value); 1710 if (ext == null) 1711 return null; 1712 else 1713 return new ExtensionContext(ext, ext.getSnapshot().getElement().get(0)); 1714 } 1715 } 1716 1717 1718 private boolean extensionIsComplex(String value) { 1719 if (value.contains("#")) { 1720 StructureDefinition ext = context.getWorker().fetchResource(StructureDefinition.class, value.substring(0, value.indexOf("#"))); 1721 if (ext == null) 1722 return false; 1723 String tail = value.substring(value.indexOf("#")+1); 1724 ElementDefinition ed = null; 1725 for (ElementDefinition ted : ext.getSnapshot().getElement()) { 1726 if (tail.equals(ted.getSliceName())) { 1727 ed = ted; 1728 break; 1729 } 1730 } 1731 if (ed == null) 1732 return false; 1733 int i = ext.getSnapshot().getElement().indexOf(ed); 1734 int j = i+1; 1735 while (j < ext.getSnapshot().getElement().size() && !ext.getSnapshot().getElement().get(j).getPath().equals(ed.getPath())) 1736 j++; 1737 return j - i > 5; 1738 } else { 1739 StructureDefinition ext = context.getWorker().fetchResource(StructureDefinition.class, value); 1740 return ext != null && ext.getSnapshot().getElement().size() > 5; 1741 } 1742 } 1743 1744 1745 1746 1747 private BindingResolution makeNullBr(ElementDefinitionBindingComponent binding) { 1748 BindingResolution br = new BindingResolution(); 1749 br.url = "http://none.none/none"; 1750 br.display = "todo"; 1751 return br; 1752 } 1753 1754 private ElementDefinitionBindingComponent makeUnifiedBinding(ElementDefinitionBindingComponent binding, ElementDefinition element) { 1755 if (!element.hasUserData(ProfileUtilities.UD_DERIVATION_POINTER)) { 1756 return binding; 1757 } 1758 ElementDefinition base = (ElementDefinition) element.getUserData(ProfileUtilities.UD_DERIVATION_POINTER); 1759 if (!base.hasBinding()) { 1760 return binding; 1761 } 1762 ElementDefinitionBindingComponent o = base.getBinding(); 1763 ElementDefinitionBindingComponent b = new ElementDefinitionBindingComponent(); 1764 b.setUserData(ProfileUtilities.UD_DERIVATION_POINTER, o); 1765 if (binding.hasValueSet()) { 1766 b.setValueSet(binding.getValueSet()); 1767 } else if (o.hasValueSet()) { 1768 b.setValueSet(o.getValueSet()); 1769 b.getValueSetElement().setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, o.getValueSetElement()); 1770 } 1771 if (binding.hasStrength()) { 1772 b.setStrength(binding.getStrength()); 1773 } else if (o.hasStrength()) { 1774 b.setStrength(o.getStrength()); 1775 b.getStrengthElement().setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, o.getStrengthElement()); 1776 } 1777 if (binding.hasDescription()) { 1778 b.setDescription(binding.getDescription()); 1779 } else if (o.hasDescription()) { 1780 b.setDescription(o.getDescription()); 1781 b.getDescriptionElement().setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, o.getDescriptionElement()); 1782 } 1783 // todo: derivation? 1784 b.getExtension().addAll(binding.getExtension()); 1785 return b; 1786 } 1787 1788 private void genFixedValue(HierarchicalTableGenerator gen, Row erow, DataType value, boolean snapshot, boolean pattern, String corePath, boolean skipnoValue) { 1789 String ref = context.getPkp().getLinkFor(corePath, value.fhirType()); 1790 if (ref != null && ref.contains(".html")) { 1791 ref = ref.substring(0, ref.indexOf(".html"))+"-definitions.html#"; 1792 } else { 1793 ref = "?gen-fv?"; 1794 } 1795 StructureDefinition sd = context.getWorker().fetchTypeDefinition(value.fhirType()); 1796 1797 for (org.hl7.fhir.r5.model.Property t : value.children()) { 1798 if (t.getValues().size() > 0 || snapshot) { 1799 ElementDefinition ed = findElementDefinition(sd, t.getName()); 1800 if (t.getValues().size() == 0 || (t.getValues().size() == 1 && t.getValues().get(0).isEmpty())) { 1801 if (!skipnoValue) { 1802 Row row = gen.new Row(); 1803 erow.getSubRows().add(row); 1804 Cell c = gen.new Cell(); 1805 row.getCells().add(c); 1806 c.addPiece(gen.new Piece((ed.getBase().getPath().equals(ed.getPath()) ? ref+ed.getPath() : corePath+(VersionUtilities.isR5Ver(context.getWorker().getVersion()) ? "types-definitions.html#"+ed.getBase().getPath() : "element-definitions.html#"+ed.getBase().getPath())), t.getName(), null)); 1807 c = gen.new Cell(); 1808 row.getCells().add(c); 1809 c.addPiece(gen.new Piece(null, null, null)); 1810 c = gen.new Cell(); 1811 row.getCells().add(c); 1812 if (!pattern) { 1813 c.addPiece(gen.new Piece(null, "0..0", null)); 1814 row.setIcon("icon_fixed.gif", "Fixed Value" /*HierarchicalTableGenerator.TEXT_ICON_FIXED*/); 1815 } else if (isPrimitive(t.getTypeCode())) { 1816 row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); 1817 c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); 1818 } else if (isReference(t.getTypeCode())) { 1819 row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); 1820 c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); 1821 } else { 1822 row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); 1823 c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); 1824 } 1825 c = gen.new Cell(); 1826 row.getCells().add(c); 1827 if (t.getTypeCode().contains("(")) { 1828 String tc = t.getTypeCode(); 1829 String tn = tc.substring(0, tc.indexOf("(")); 1830 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, tn), tn, null)); 1831 c.addPiece(gen.new Piece(null, "(", null)); 1832 String[] p = tc.substring(tc.indexOf("(")+1, tc.indexOf(")")).split("\\|"); 1833 for (String s : p) { 1834 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, s), s, null)); 1835 } 1836 c.addPiece(gen.new Piece(null, ")", null)); 1837 } else { 1838 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, t.getTypeCode()), t.getTypeCode(), null)); 1839 } 1840 c = gen.new Cell(); 1841 c.addPiece(gen.new Piece(null, ed.getShort(), null)); 1842 row.getCells().add(c); 1843 } 1844 } else { 1845 for (Base b : t.getValues()) { 1846 Row row = gen.new Row(); 1847 erow.getSubRows().add(row); 1848 row.setIcon("icon_fixed.gif", "Fixed Value" /*HierarchicalTableGenerator.TEXT_ICON_FIXED*/); 1849 1850 Cell c = gen.new Cell(); 1851 row.getCells().add(c); 1852 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)); 1853 1854 c = gen.new Cell(); 1855 row.getCells().add(c); 1856 c.addPiece(gen.new Piece(null, null, null)); 1857 1858 c = gen.new Cell(); 1859 row.getCells().add(c); 1860 if (pattern) 1861 c.addPiece(gen.new Piece(null, "1.."+(t.getMaxCardinality() == 2147483647 ? "*" : Integer.toString(t.getMaxCardinality())), null)); 1862 else 1863 c.addPiece(gen.new Piece(null, "1..1", null)); 1864 1865 c = gen.new Cell(); 1866 row.getCells().add(c); 1867 if (b.fhirType().contains("(")) { 1868 String tc = b.fhirType(); 1869 String tn = tc.substring(0, tc.indexOf("(")); 1870 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, tn), tn, null)); 1871 c.addPiece(gen.new Piece(null, "(", null)); 1872 String[] p = tc.substring(tc.indexOf("(")+1, tc.indexOf(")")).split("\\|"); 1873 for (String s : p) { 1874 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, s), s, null)); 1875 } 1876 c.addPiece(gen.new Piece(null, ")", null)); 1877 } else { 1878 c.addPiece(gen.new Piece(context.getPkp().getLinkFor(corePath, b.fhirType()), b.fhirType(), null)); 1879 } 1880 1881 if (b.isPrimitive()) { 1882 c = gen.new Cell(); 1883 row.getCells().add(c); 1884 c.addPiece(gen.new Piece(null, ed.getShort(), null)); 1885 c.addPiece(gen.new Piece("br")); 1886 c.getPieces().add(gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight: bold")); 1887 String s = b.primitiveValue(); 1888 // ok. let's see if we can find a relevant link for this 1889 String link = null; 1890 if (Utilities.isAbsoluteUrl(s)) { 1891 link = context.getPkp().getLinkForUrl(corePath, s); 1892 } 1893 c.getPieces().add(gen.new Piece(link, s, null).addStyle("color: darkgreen")); 1894 } else { 1895 c = gen.new Cell(); 1896 row.getCells().add(c); 1897 c.addPiece(gen.new Piece(null, ed.getShort(), null)); 1898 c.addPiece(gen.new Piece("br")); 1899 c.getPieces().add(gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight: bold")); 1900 c.getPieces().add(gen.new Piece(null, "(complex)", null).addStyle("color: darkgreen")); 1901 genFixedValue(gen, row, (DataType) b, snapshot, pattern, corePath, skipnoValue); 1902 } 1903 } 1904 } 1905 } 1906 } 1907 } 1908 1909 1910 private ElementDefinition findElementDefinition(StructureDefinition sd, String name) { 1911 String path = sd.getTypeName()+"."+name; 1912 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 1913 if (ed.getPath().equals(path)) 1914 return ed; 1915 } 1916 throw new FHIRException(context.getWorker().formatMessage(I18nConstants.UNABLE_TO_FIND_ELEMENT_, path)); 1917 } 1918 1919 1920 private String getFixedUrl(StructureDefinition sd) { 1921 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 1922 if (ed.getPath().equals("Extension.url")) { 1923 if (ed.hasFixed() && ed.getFixed() instanceof UriType) 1924 return ed.getFixed().primitiveValue(); 1925 } 1926 } 1927 return null; 1928 } 1929 1930 1931 private Piece describeCoded(HierarchicalTableGenerator gen, DataType fixed) { 1932 if (fixed instanceof Coding) { 1933 Coding c = (Coding) fixed; 1934 ValidationResult vr = context.getWorker().validateCode(context.getTerminologyServiceOptions(), c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay()); 1935 if (vr.getDisplay() != null) 1936 return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); 1937 } else if (fixed instanceof CodeableConcept) { 1938 CodeableConcept cc = (CodeableConcept) fixed; 1939 for (Coding c : cc.getCoding()) { 1940 ValidationResult vr = context.getWorker().validateCode(context.getTerminologyServiceOptions(), c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay()); 1941 if (vr.getDisplay() != null) 1942 return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen"); 1943 } 1944 } 1945 return null; 1946 } 1947 1948 1949 private boolean hasDescription(DataType fixed) { 1950 if (fixed instanceof Coding) { 1951 return ((Coding) fixed).hasDisplay(); 1952 } else if (fixed instanceof CodeableConcept) { 1953 CodeableConcept cc = (CodeableConcept) fixed; 1954 if (cc.hasText()) 1955 return true; 1956 for (Coding c : cc.getCoding()) 1957 if (c.hasDisplay()) 1958 return true; 1959 } // (fixed instanceof CodeType) || (fixed instanceof Quantity); 1960 return false; 1961 } 1962 1963 1964 private boolean isCoded(DataType fixed) { 1965 return (fixed instanceof Coding) || (fixed instanceof CodeableConcept) || (fixed instanceof CodeType) || (fixed instanceof Quantity); 1966 } 1967 1968 1969 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 { 1970 Cell c = gen.new Cell(); 1971 row.getCells().add(c); 1972 1973 if (used) { 1974 if (definition.hasContentReference()) { 1975 ElementInStructure ed = getElementByName(profile.getSnapshot().getElement(), definition.getContentReference(), profile); 1976 if (ed == null) 1977 c.getPieces().add(gen.new Piece(null, "Unknown reference to "+definition.getContentReference(), null)); 1978 else { 1979 if (ed.getSource() == profile) { 1980 c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), "See "+ed.getElement().getPath(), null)); 1981 } else { 1982 c.getPieces().add(gen.new Piece(ed.getSource().getUserData("path")+"#"+ed.getElement().getPath(), "See "+ed.getSource().getTypeName()+"."+ed.getElement().getPath(), null)); 1983 } 1984 } 1985 } 1986 if (definition.getPath().endsWith("url") && definition.hasFixed()) { 1987 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen"))); 1988 } else { 1989 if (url != null) { 1990 if (!c.getPieces().isEmpty()) 1991 c.addPiece(gen.new Piece("br")); 1992 String fullUrl = url.startsWith("#") ? baseURL+url : url; 1993 StructureDefinition ed = context.getWorker().fetchResource(StructureDefinition.class, url, profile); 1994 String ref = null; 1995 if (ed != null) { 1996 String p = ed.getUserString("path"); 1997 if (p != null) { 1998 ref = p.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p : Utilities.pathURL(corePath, p); 1999 } 2000 } 2001 c.getPieces().add(gen.new Piece(null, "URL: ", null).addStyle("font-weight:bold")); 2002 c.getPieces().add(gen.new Piece(ref, fullUrl, null)); 2003 } 2004 2005 if (definition.hasSlicing()) { 2006 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2007 c.getPieces().add(gen.new Piece(null, "Slice: ", null).addStyle("font-weight:bold")); 2008 c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); 2009 } 2010 if (definition != null) { 2011 ElementDefinitionBindingComponent binding = null; 2012 if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty()) 2013 binding = valueDefn.getBinding(); 2014 else if (definition.hasBinding()) 2015 binding = definition.getBinding(); 2016 if (binding!=null && !binding.isEmpty()) { 2017 if (!c.getPieces().isEmpty()) 2018 c.addPiece(gen.new Piece("br")); 2019 BindingResolution br = context.getPkp().resolveBinding(profile, binding, definition.getPath()); 2020 c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, "Binding: ", null).addStyle("font-weight:bold"))); 2021 c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, null))); 2022 if (binding.hasStrength()) { 2023 c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, " (", null))); 2024 c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), binding.getStrength().toCode(), binding.getStrength().getDefinition()))); c.getPieces().add(gen.new Piece(null, ")", null)); 2025 } 2026 if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) { 2027 c.getPieces().add(gen.new Piece(null, ": ", null)); 2028 c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context.getWorker(), binding.getDescriptionElement()).asStringValue()); 2029 } 2030 } 2031 for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { 2032 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2033 c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold"))); 2034 if (inv.getHumanElement().hasExtension(ToolingExtensions.EXT_REND_MD)) { 2035 c.addMarkdown(inv.getHumanElement().getExtensionString(ToolingExtensions.EXT_REND_MD)); 2036 } else { 2037 c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getHuman(), null))); 2038 } 2039 } 2040 if (definition.hasFixed()) { 2041 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2042 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight:bold"))); 2043 String s = buildJson(definition.getFixed()); 2044 String link = null; 2045 if (Utilities.isAbsoluteUrl(s)) 2046 link = context.getPkp().getLinkForUrl(corePath, s); 2047 c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); 2048 } else if (definition.hasPattern()) { 2049 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2050 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "Required Pattern: ", null).addStyle("font-weight:bold"))); 2051 c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); 2052 } else if (definition.hasExample()) { 2053 for (ElementDefinitionExampleComponent ex : definition.getExample()) { 2054 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2055 c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, "Example'"+("".equals("General")? "" : " "+ex.getLabel()+"'")+": ", null).addStyle("font-weight:bold"))); 2056 c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); 2057 } 2058 } 2059 if (definition.hasMaxLength() && definition.getMaxLength()!=0) { 2060 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2061 c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold"))); 2062 c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); 2063 } 2064 if (profile != null) { 2065 for (StructureDefinitionMappingComponent md : profile.getMapping()) { 2066 if (md.hasExtension(ToolingExtensions.EXT_TABLE_NAME)) { 2067 ElementDefinitionMappingComponent map = null; 2068 for (ElementDefinitionMappingComponent m : definition.getMapping()) 2069 if (m.getIdentity().equals(md.getIdentity())) 2070 map = m; 2071 if (map != null) { 2072 for (int i = 0; i<definition.getMapping().size(); i++){ 2073 c.addPiece(gen.new Piece("br")); 2074 c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(md, ToolingExtensions.EXT_TABLE_NAME)+": " + map.getMap(), null)); 2075 } 2076 } 2077 } 2078 } 2079 } 2080 if (definition.hasDefinition()) { 2081 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2082 c.getPieces().add(gen.new Piece(null, "Definition: ", null).addStyle("font-weight:bold")); 2083 c.addPiece(gen.new Piece("br")); 2084 c.addMarkdown(definition.getDefinition()); 2085// c.getPieces().add(checkForNoChange(definition.getCommentElement(), gen.new Piece(null, definition.getComment(), null))); 2086 } 2087 if (definition.getComment()!=null) { 2088 if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } 2089 c.getPieces().add(gen.new Piece(null, "Comments: ", null).addStyle("font-weight:bold")); 2090 c.addPiece(gen.new Piece("br")); 2091 c.addMarkdown(definition.getComment()); 2092// c.getPieces().add(checkForNoChange(definition.getCommentElement(), gen.new Piece(null, definition.getComment(), null))); 2093 } 2094 } 2095 } 2096 } 2097 return c; 2098 } 2099 2100 private boolean onlyInformationIsMapping(List<ElementDefinition> list, ElementDefinition e) { 2101 return (!e.hasSliceName() && !e.hasSlicing() && (onlyInformationIsMapping(e))) && 2102 getChildren(list, e).isEmpty(); 2103 } 2104 2105 private boolean onlyInformationIsMapping(ElementDefinition d) { 2106 return !d.hasShort() && !d.hasDefinition() && 2107 !d.hasRequirements() && !d.getAlias().isEmpty() && !d.hasMinElement() && 2108 !d.hasMax() && !d.getType().isEmpty() && !d.hasContentReference() && 2109 !d.hasExample() && !d.hasFixed() && !d.hasMaxLengthElement() && 2110 !d.getCondition().isEmpty() && !d.getConstraint().isEmpty() && !d.hasMustSupportElement() && 2111 !d.hasBinding(); 2112 } 2113 2114 private boolean allAreReference(List<TypeRefComponent> types) { 2115 for (TypeRefComponent t : types) { 2116 if (!t.hasTarget()) 2117 return false; 2118 } 2119 return true; 2120 } 2121 2122 private List<ElementDefinition> getChildren(List<ElementDefinition> all, ElementDefinition element) { 2123 List<ElementDefinition> result = new ArrayList<ElementDefinition>(); 2124 int i = all.indexOf(element)+1; 2125 while (i < all.size() && all.get(i).getPath().length() > element.getPath().length()) { 2126 if ((all.get(i).getPath().substring(0, element.getPath().length()+1).equals(element.getPath()+".")) && !all.get(i).getPath().substring(element.getPath().length()+1).contains(".")) 2127 result.add(all.get(i)); 2128 i++; 2129 } 2130 return result; 2131 } 2132 2133 2134 protected String tail(String path) { 2135 if (path == null) { 2136 return ""; 2137 } else if (path.contains(".")) 2138 return path.substring(path.lastIndexOf('.')+1); 2139 else 2140 return path; 2141 } 2142 2143 2144 2145 protected boolean isPrimitive(String value) { 2146 StructureDefinition sd = context.getWorker().fetchTypeDefinition(value); 2147 if (sd == null) // might be running before all SDs are available 2148 return Utilities.existsInList(value, "base64Binary", "boolean", "canonical", "code", "date", "dateTime", "decimal", "id", "instant", "integer", "integer64", "markdown", "oid", "positiveInt", "string", "time", "unsignedInt", "uri", "url", "uuid"); 2149 else 2150 return sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; 2151 } 2152 2153 2154 private boolean isDataType(String value) { 2155 StructureDefinition sd = context.getWorker().fetchTypeDefinition(value); 2156 if (sd == null) // might be running before all SDs are available 2157 return Utilities.existsInList(value, "Address", "Age", "Annotation", "Attachment", "CodeableConcept", "Coding", "ContactPoint", "Count", "Distance", "Duration", "HumanName", "Identifier", "Money", "Period", "Quantity", "Range", "Ratio", "Reference", "SampledData", "Signature", "Timing", 2158 "ContactDetail", "Contributor", "DataRequirement", "Expression", "ParameterDefinition", "RelatedArtifact", "TriggerDefinition", "UsageContext"); 2159 else 2160 return sd.getKind() == StructureDefinitionKind.COMPLEXTYPE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION; 2161 } 2162 2163 private boolean slicesExist(List<ElementDefinition> elements, ElementDefinition element) { 2164 if (elements == null) { 2165 return true; 2166 } 2167 boolean found = false; 2168 int start = elements.indexOf(element); 2169 if (start < 0) { 2170 return false; 2171 } 2172 for (int i = start; i < elements.size(); i++) { 2173 ElementDefinition ed = elements.get(i); 2174 if (ed.getPath().equals(element.getPath())) { 2175 if (ed.hasSliceName()) { 2176 found = true; 2177 } 2178 } 2179 if (ed.getPath().length() < element.getPath().length()) { 2180 break; 2181 } 2182 } 2183 return found; 2184 } 2185 2186 2187 private Cell addCell(Row row, Cell cell) { 2188 row.getCells().add(cell); 2189 return (cell); 2190 } 2191 2192 private String checkAdd(String src, String app) { 2193 return app == null ? src : src + app; 2194 } 2195 2196 public boolean hasNonBaseConditions(List<IdType> conditions) { 2197 for (IdType c : conditions) { 2198 if (!isBaseCondition(c)) { 2199 return true; 2200 } 2201 } 2202 return false; 2203 } 2204 2205 2206 public boolean hasNonBaseConstraints(List<ElementDefinitionConstraintComponent> constraints) { 2207 for (ElementDefinitionConstraintComponent c : constraints) { 2208 if (!isBaseConstraint(c)) { 2209 return true; 2210 } 2211 } 2212 return false; 2213 } 2214 2215 public String listConstraintsAndConditions(ElementDefinition element) { 2216 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 2217 for (ElementDefinitionConstraintComponent con : element.getConstraint()) { 2218 if (!isBaseConstraint(con)) { 2219 b.append(con.getKey()); 2220 } 2221 } 2222 for (IdType id : element.getCondition()) { 2223 if (!isBaseCondition(id)) { 2224 b.append(id.asStringValue()); 2225 } 2226 } 2227 return b.toString(); 2228 } 2229 2230 private boolean isBaseCondition(IdType c) { 2231 String key = c.asStringValue(); 2232 return key != null && (key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-")); 2233 } 2234 2235 private boolean isBaseConstraint(ElementDefinitionConstraintComponent con) { 2236 String key = con.getKey(); 2237 return key != null && (key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-")); 2238 } 2239 2240 private void makeChoiceRows(List<Row> subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath, String profileBaseFileName, boolean mustSupportMode, Resource src) { 2241 // create a child for each choice 2242 for (TypeRefComponent tr : element.getType()) { 2243 if (!mustSupportMode || allTypesMustSupport(element) || isMustSupport(tr)) { 2244 Row choicerow = gen.new Row(); 2245 String t = tr.getWorkingCode(); 2246 if (isReference(t)) { 2247 choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), null, null)); 2248 choicerow.getCells().add(gen.new Cell()); 2249 choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); 2250 choicerow.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); 2251 Cell c = gen.new Cell(); 2252 choicerow.getCells().add(c); 2253 if (ADD_REFERENCE_TO_TABLE) { 2254 if (tr.getWorkingCode().equals("canonical")) 2255 c.getPieces().add(gen.new Piece(corePath+"datatypes.html#canonical", "canonical", null)); 2256 else 2257 c.getPieces().add(gen.new Piece(corePath+"references.html#Reference", "Reference", null)); 2258 if (!mustSupportMode && isMustSupportDirect(tr) && element.getMustSupport()) { 2259 c.addPiece(gen.new Piece(null, " ", null)); 2260 c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); 2261 } 2262 c.getPieces().add(gen.new Piece(null, "(", null)); 2263 } 2264 boolean first = true; 2265 for (CanonicalType rt : tr.getTargetProfile()) { 2266 if (!mustSupportMode || allProfilesMustSupport(tr.getTargetProfile()) || isMustSupport(rt)) { 2267 if (!first) 2268 c.getPieces().add(gen.new Piece(null, " | ", null)); 2269 genTargetLink(gen, profileBaseFileName, corePath, c, tr, rt.getValue(), src); 2270 if (!mustSupportMode && isMustSupport(rt) && element.getMustSupport()) { 2271 c.addPiece(gen.new Piece(null, " ", null)); 2272 c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); 2273 } 2274 first = false; 2275 } 2276 } 2277 if (first) { 2278 c.getPieces().add(gen.new Piece(null, "Any", null)); 2279 } 2280 2281 if (ADD_REFERENCE_TO_TABLE) { 2282 c.getPieces().add(gen.new Piece(null, ")", null)); 2283 } 2284 2285 } else { 2286 StructureDefinition sd = context.getWorker().fetchTypeDefinition(t); 2287 if (sd == null) { 2288 System.out.println("Unable to find "+t); 2289 sd = context.getWorker().fetchTypeDefinition(t); 2290 } else if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { 2291 choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); 2292 choicerow.getCells().add(gen.new Cell()); 2293 choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); 2294 choicerow.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); 2295 Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getTypeName(), null, null); 2296 choicerow.getCells().add(c); 2297 if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { 2298 c.addPiece(gen.new Piece(null, " ", null)); 2299 c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); 2300 } 2301 } else { 2302 choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); 2303 choicerow.getCells().add(gen.new Cell()); 2304 choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); 2305 choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); 2306 Cell c = gen.new Cell(null, context.getPkp().getLinkFor(corePath, t), sd.getTypeName(), null, null); 2307 choicerow.getCells().add(c); 2308 if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { 2309 c.addPiece(gen.new Piece(null, " ", null)); 2310 c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); 2311 } 2312 } 2313 if (tr.hasProfile()) { 2314 Cell typeCell = choicerow.getCells().get(3); 2315 typeCell.addPiece(gen.new Piece(null, "(", null)); 2316 boolean first = true; 2317 for (CanonicalType pt : tr.getProfile()) { 2318 if (!mustSupportMode || allProfilesMustSupport(tr.getProfile()) || isMustSupport(pt)) { 2319 if (first) first = false; else typeCell.addPiece(gen.new Piece(null, " | ", null)); 2320 StructureDefinition psd = context.getWorker().fetchResource(StructureDefinition.class, pt.getValue(), src); 2321 if (psd == null) 2322 typeCell.addPiece(gen.new Piece(null, "?gen-e2?", null)); 2323 else 2324 typeCell.addPiece(gen.new Piece(psd.getUserString("path"), psd.getName(), psd.present())); 2325 if (!mustSupportMode && isMustSupport(pt) && element.getMustSupport()) { 2326 typeCell.addPiece(gen.new Piece(null, " ", null)); 2327 typeCell.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); 2328 } 2329 } 2330 } 2331 typeCell.addPiece(gen.new Piece(null, ")", null)); 2332 } 2333 } 2334 choicerow.getCells().add(gen.new Cell()); 2335 subRows.add(choicerow); 2336 } 2337 } 2338 } 2339 2340 private boolean isReference(String t) { 2341 return t.equals("Reference") || t.equals("canonical"); 2342 } 2343 2344 2345 2346 private List<ElementChoiceGroup> readChoices(ElementDefinition ed, List<ElementDefinition> children) { 2347 List<ElementChoiceGroup> result = new ArrayList<>(); 2348 for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { 2349 ElementChoiceGroup grp = context.getProfileUtilities().processConstraint(children, c); 2350 if (grp != null) { 2351 result.add(grp); 2352 } 2353 } 2354 return result; 2355 } 2356 2357 private Piece checkForNoChange(Element src1, Element src2, Piece piece) { 2358 if (src1.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS) && src2.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS)) { 2359 piece.addStyle("opacity: 0.5"); 2360 } 2361 return piece; 2362 } 2363 2364 2365 private String buildJson(DataType value) throws IOException { 2366 if (value instanceof PrimitiveType) 2367 return ((PrimitiveType<?>) value).asStringValue(); 2368 2369 IParser json = new JsonParser(); 2370 return json.composeString(value, null); 2371 } 2372 2373 private String describeSlice(ElementDefinitionSlicingComponent slicing) { 2374 return translate("sd.table", "%s, %s by %s", slicing.getOrdered() ? translate("sd.table", "Ordered") : translate("sd.table", "Unordered"), describe(slicing.getRules()), commas(slicing.getDiscriminator())); 2375 } 2376 2377 2378 2379 private String commas(List<ElementDefinitionSlicingDiscriminatorComponent> list) { 2380 CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder(); 2381 for (ElementDefinitionSlicingDiscriminatorComponent id : list) 2382 c.append((id.hasType() ? id.getType().toCode() : "??")+":"+id.getPath()); 2383 return c.toString(); 2384 } 2385 2386 2387 private String describe(SlicingRules rules) { 2388 if (rules == null) 2389 return translate("sd.table", "Unspecified"); 2390 switch (rules) { 2391 case CLOSED : return translate("sd.table", "Closed"); 2392 case OPEN : return translate("sd.table", "Open"); 2393 case OPENATEND : return translate("sd.table", "Open At End"); 2394 default: 2395 return "?gen-sr?"; 2396 } 2397 } 2398 2399 private boolean allTypesMustSupport(ElementDefinition e) { 2400 boolean all = true; 2401 boolean any = false; 2402 for (TypeRefComponent tr : e.getType()) { 2403 all = all && isMustSupport(tr); 2404 any = any || isMustSupport(tr); 2405 } 2406 return !all && !any; 2407 } 2408 2409 private boolean allProfilesMustSupport(List<CanonicalType> profiles) { 2410 boolean all = true; 2411 boolean any = false; 2412 for (CanonicalType u : profiles) { 2413 all = all && isMustSupport(u); 2414 any = any || isMustSupport(u); 2415 } 2416 return !all && !any; 2417 } 2418 public boolean isMustSupportDirect(TypeRefComponent tr) { 2419 return ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))); 2420 } 2421 2422 public boolean isMustSupport(TypeRefComponent tr) { 2423 if ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))) { 2424 return true; 2425 } 2426 if (isMustSupport(tr.getProfile())) { 2427 return true; 2428 } 2429 return isMustSupport(tr.getTargetProfile()); 2430 } 2431 2432 public boolean isMustSupport(List<CanonicalType> profiles) { 2433 for (CanonicalType ct : profiles) { 2434 if (isMustSupport(ct)) { 2435 return true; 2436 } 2437 } 2438 return false; 2439 } 2440 2441 2442 public boolean isMustSupport(CanonicalType profile) { 2443 return "true".equals(ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_MUST_SUPPORT)); 2444 } 2445 2446 2447 2448 private SpanEntry buildSpanEntryFromProfile(String name, String cardinality, StructureDefinition profile) throws IOException { 2449 SpanEntry res = new SpanEntry(); 2450 res.setName(name); 2451 res.setCardinality(cardinality); 2452 res.setProfileLink(profile.getUserString("path")); 2453 res.setResType(profile.getTypeName()); 2454 StructureDefinition base = context.getWorker().fetchResource(StructureDefinition.class, res.getResType()); 2455 if (base != null) 2456 res.setResLink(base.getUserString("path")); 2457 res.setId(profile.getId()); 2458 res.setProfile(profile.getDerivation() == TypeDerivationRule.CONSTRAINT); 2459 StringBuilder b = new StringBuilder(); 2460 b.append(res.getResType()); 2461 boolean first = true; 2462 boolean open = false; 2463 if (profile.getDerivation() == TypeDerivationRule.CONSTRAINT) { 2464 res.setDescription(profile.getName()); 2465 for (ElementDefinition ed : profile.getSnapshot().getElement()) { 2466 if (isKeyProperty(ed.getBase().getPath()) && ed.hasFixed()) { 2467 if (first) { 2468 open = true; 2469 first = false; 2470 b.append("["); 2471 } else { 2472 b.append(", "); 2473 } 2474 b.append(tail(ed.getBase().getPath())); 2475 b.append("="); 2476 b.append(summarize(ed.getFixed())); 2477 } 2478 } 2479 if (open) 2480 b.append("]"); 2481 } else 2482 res.setDescription("Base FHIR "+profile.getName()); 2483 res.setType(b.toString()); 2484 return res ; 2485 } 2486 2487 2488 private String summarize(DataType value) throws IOException { 2489 if (value instanceof Coding) 2490 return summarizeCoding((Coding) value); 2491 else if (value instanceof CodeableConcept) 2492 return summarizeCodeableConcept((CodeableConcept) value); 2493 else 2494 return buildJson(value); 2495 } 2496 2497 2498 private String summarizeCoding(Coding value) { 2499 String uri = value.getSystem(); 2500 String system = TerminologyRenderer.describeSystem(uri); 2501 if (Utilities.isURL(system)) { 2502 if (system.equals("http://cap.org/protocols")) 2503 system = "CAP Code"; 2504 } 2505 return system+" "+value.getCode(); 2506 } 2507 2508 2509 private String summarizeCodeableConcept(CodeableConcept value) { 2510 if (value.hasCoding()) 2511 return summarizeCoding(value.getCodingFirstRep()); 2512 else 2513 return value.getText(); 2514 } 2515 2516 2517 private boolean isKeyProperty(String path) { 2518 return Utilities.existsInList(path, "Observation.code"); 2519 } 2520 2521 2522 private TableModel initSpanningTable(HierarchicalTableGenerator gen, String prefix, boolean isLogical, String id) { 2523 TableModel model = gen.new TableModel(id, true); 2524 2525 model.setDocoImg(prefix+"help16.png"); 2526 model.setDocoRef(Utilities.pathURL(prefix, "formats.html#table")); // todo: change to graph definition 2527 model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Property", "A profiled resource", null, 0)); 2528 model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Card.", "Minimum and Maximum # of times the the element can appear in the instance", null, 0)); 2529 model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Content", "What goes here", null, 0)); 2530 model.getTitles().add(gen.new Title(null, model.getDocoRef(), "Description", "Description of the profile", null, 0)); 2531 return model; 2532 } 2533 2534 private void genSpanEntry(HierarchicalTableGenerator gen, List<Row> rows, SpanEntry span) throws IOException { 2535 Row row = gen.new Row(); 2536 rows.add(row); 2537 row.setAnchor(span.getId()); 2538 //row.setColor(..?); 2539 if (span.isProfile()) { 2540 row.setIcon("icon_profile.png", HierarchicalTableGenerator.TEXT_ICON_PROFILE); 2541 } else { 2542 row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); 2543 } 2544 2545 row.getCells().add(gen.new Cell(null, null, span.getName(), null, null)); 2546 row.getCells().add(gen.new Cell(null, null, span.getCardinality(), null, null)); 2547 row.getCells().add(gen.new Cell(null, span.getProfileLink(), span.getType(), null, null)); 2548 row.getCells().add(gen.new Cell(null, null, span.getDescription(), null, null)); 2549 2550 for (SpanEntry child : span.getChildren()) { 2551 genSpanEntry(gen, row.getSubRows(), child); 2552 } 2553 } 2554 2555 2556 public XhtmlNode generateSpanningTable(StructureDefinition profile, String imageFolder, boolean onlyConstraints, String constraintPrefix, Set<String> outputTracker) throws IOException, FHIRException { 2557 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, false, true); 2558 gen.setTranslator(getTranslator()); 2559 TableModel model = initSpanningTable(gen, "", false, profile.getId()); 2560 Set<String> processed = new HashSet<String>(); 2561 SpanEntry span = buildSpanningTable("(focus)", "", profile, processed, onlyConstraints, constraintPrefix); 2562 2563 genSpanEntry(gen, model.getRows(), span); 2564 return gen.generate(model, "", 0, outputTracker); 2565 } 2566 2567 private SpanEntry buildSpanningTable(String name, String cardinality, StructureDefinition profile, Set<String> processed, boolean onlyConstraints, String constraintPrefix) throws IOException { 2568 SpanEntry res = buildSpanEntryFromProfile(name, cardinality, profile); 2569 boolean wantProcess = !processed.contains(profile.getUrl()); 2570 processed.add(profile.getUrl()); 2571 if (wantProcess && profile.getDerivation() == TypeDerivationRule.CONSTRAINT) { 2572 for (ElementDefinition ed : profile.getSnapshot().getElement()) { 2573 if (!"0".equals(ed.getMax()) && ed.getType().size() > 0) { 2574 String card = getCardinality(ed, profile.getSnapshot().getElement()); 2575 if (!card.endsWith(".0")) { 2576 List<String> refProfiles = listReferenceProfiles(ed); 2577 if (refProfiles.size() > 0) { 2578 String uri = refProfiles.get(0); 2579 if (uri != null) { 2580 StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, uri); 2581 if (sd != null && (!onlyConstraints || (sd.getDerivation() == TypeDerivationRule.CONSTRAINT && (constraintPrefix == null || sd.getUrl().startsWith(constraintPrefix))))) { 2582 res.getChildren().add(buildSpanningTable(nameForElement(ed), card, sd, processed, onlyConstraints, constraintPrefix)); 2583 } 2584 } 2585 } 2586 } 2587 } 2588 } 2589 } 2590 return res; 2591 } 2592 2593 2594 private String getCardinality(ElementDefinition ed, List<ElementDefinition> list) { 2595 int min = ed.getMin(); 2596 int max = !ed.hasMax() || ed.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(ed.getMax()); 2597 ElementDefinition ned = ed; 2598 while (ned != null && ned.getPath().contains(".")) { 2599 ned = findParent(ned, list); 2600 if (ned != null) { // todo: this can happen if we've walked into a resoruce. Not sure what to about that? 2601 if ("0".equals(ned.getMax())) 2602 max = 0; 2603 else if (!ned.getMax().equals("1") && !ned.hasSlicing()) 2604 max = Integer.MAX_VALUE; 2605 if (ned.getMin() == 0) { 2606 min = 0; 2607 } 2608 } 2609 } 2610 return Integer.toString(min)+".."+(max == Integer.MAX_VALUE ? "*" : Integer.toString(max)); 2611 } 2612 2613 2614 private ElementDefinition findParent(ElementDefinition ed, List<ElementDefinition> list) { 2615 int i = list.indexOf(ed)-1; 2616 while (i >= 0 && !ed.getPath().startsWith(list.get(i).getPath()+".")) 2617 i--; 2618 if (i == -1) 2619 return null; 2620 else 2621 return list.get(i); 2622 } 2623 2624 2625 private List<String> listReferenceProfiles(ElementDefinition ed) { 2626 List<String> res = new ArrayList<String>(); 2627 for (TypeRefComponent tr : ed.getType()) { 2628 // code is null if we're dealing with "value" and profile is null if we just have Reference() 2629 if (tr.hasTarget() && tr.hasTargetProfile()) 2630 for (UriType u : tr.getTargetProfile()) 2631 res.add(u.getValue()); 2632 } 2633 return res; 2634 } 2635 2636 2637 private String nameForElement(ElementDefinition ed) { 2638 return ed.getPath().substring(ed.getPath().indexOf(".")+1); 2639 } 2640 2641 public static String formatTypeSpecifiers(IWorkerContext context, ElementDefinition d) { 2642 StringBuilder b = new StringBuilder(); 2643 boolean first = true; 2644 for (Extension e : d.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { 2645 if (first) first = false; else b.append("<br/>"); 2646 String cond = ToolingExtensions.readStringExtension(e, "condition"); 2647 String type = ToolingExtensions.readStringExtension(e, "type"); 2648 b.append("If <code>"); 2649 b.append(Utilities.escapeXml(cond)); 2650 b.append("</code> then the type is "); 2651 StructureDefinition sd = context.fetchTypeDefinition(type); 2652 if (sd == null) { 2653 b.append("<code>"); 2654 b.append(Utilities.escapeXml(type)); 2655 b.append("</code>"); 2656 } else { 2657 b.append("<a href=\""); 2658 b.append(sd.getUserString("path")); 2659 b.append("\">"); 2660 b.append(Utilities.escapeXml(sd.getTypeName())); 2661 b.append("</a>"); 2662 } 2663 } 2664 return b.toString(); 2665 } 2666 2667 public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath, String imagePath, Set<String> outputTracker, RenderingContext rc) throws IOException, FHIRException { 2668 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); 2669 gen.setTranslator(getTranslator()); 2670 TableModel model = gen.initNormalTable(corePath, false, true, ed.getId()+(full ? "f" : "n"), true); 2671 2672 boolean deep = false; 2673 String m = ""; 2674 boolean vdeep = false; 2675 if (ed.getSnapshot().getElementFirstRep().getIsModifier()) 2676 m = "modifier_"; 2677 for (ElementDefinition eld : ed.getSnapshot().getElement()) { 2678 deep = deep || eld.getPath().contains("Extension.extension."); 2679 vdeep = vdeep || eld.getPath().contains("Extension.extension.extension."); 2680 } 2681 Row r = gen.new Row(); 2682 model.getRows().add(r); 2683 String en; 2684 if (!full) 2685 en = ed.getName(); 2686 else if (ed.getSnapshot().getElement().get(0).getIsModifier()) 2687 en = "modifierExtension"; 2688 else 2689 en = "extension"; 2690 2691 r.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#extension."+ed.getName(), en, null, null)); 2692 r.getCells().add(gen.new Cell()); 2693 r.getCells().add(gen.new Cell(null, null, describeCardinality(ed.getSnapshot().getElement().get(0), null, new UnusedTracker()), null, null)); 2694 2695 ElementDefinition ved = null; 2696 if (full || vdeep) { 2697 r.getCells().add(gen.new Cell("", "", "Extension", null, null)); 2698 2699 r.setIcon(deep ? "icon_"+m+"extension_complex.png" : "icon_extension_simple.png", deep ? HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX : HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); 2700 List<ElementDefinition> children = getChildren(ed.getSnapshot().getElement(), ed.getSnapshot().getElement().get(0)); 2701 for (ElementDefinition child : children) 2702 if (!child.getPath().endsWith(".id")) { 2703 List<StructureDefinition> sdl = new ArrayList<>(); 2704 sdl.add(ed); 2705 genElement(defFile == null ? "" : defFile+"-definitions.html#extension.", gen, r.getSubRows(), child, ed.getSnapshot().getElement(), sdl, true, defFile, true, full, corePath, imagePath, true, false, false, false, null, false, rc, "", ed, null); 2706 } 2707 } else if (deep) { 2708 List<ElementDefinition> children = new ArrayList<ElementDefinition>(); 2709 for (ElementDefinition ted : ed.getSnapshot().getElement()) { 2710 if (ted.getPath().equals("Extension.extension")) 2711 children.add(ted); 2712 } 2713 2714 r.getCells().add(gen.new Cell("", "", "Extension", null, null)); 2715 r.setIcon("icon_"+m+"extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); 2716 2717 for (ElementDefinition c : children) { 2718 ved = getValueFor(ed, c); 2719 ElementDefinition ued = getUrlFor(ed, c); 2720 if (ved != null && ued != null) { 2721 Row r1 = gen.new Row(); 2722 r.getSubRows().add(r1); 2723 r1.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#"+ed.getId()+"."+c.getId(), ((UriType) ued.getFixed()).getValue(), null, null)); 2724 r1.getCells().add(gen.new Cell()); 2725 r1.getCells().add(gen.new Cell(null, null, describeCardinality(c, null, new UnusedTracker()), null, null)); 2726 genTypes(gen, r1, ved, defFile, ed, corePath, imagePath, false, false); 2727 r1.setIcon("icon_"+m+"extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); 2728 generateDescription(gen, r1, c, null, true, corePath, corePath, ed, corePath, imagePath, false, false, false, ved, false, false, false, rc); 2729 } 2730 } 2731 } else { 2732 for (ElementDefinition ted : ed.getSnapshot().getElement()) { 2733 if (ted.getPath().startsWith("Extension.value")) 2734 ved = ted; 2735 } 2736 2737 genTypes(gen, r, ved, defFile, ed, corePath, imagePath, false, false); 2738 2739 r.setIcon("icon_"+m+"extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); 2740 } 2741 Cell c = gen.new Cell("", "", "URL = "+ed.getUrl(), null, null); 2742 Piece cc = gen.new Piece(null, ed.getName()+": ", null); 2743 c.addPiece(gen.new Piece("br")).addPiece(cc); 2744 c.addMarkdown(ed.getDescription()); 2745 2746 if (!full && !(deep || vdeep) && ved != null && ved.hasBinding()) { 2747 c.addPiece(gen.new Piece("br")); 2748 BindingResolution br = context.getPkp().resolveBinding(ed, ved.getBinding(), ved.getPath()); 2749 c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold"))); 2750 c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, null))); 2751 if (ved.getBinding().hasStrength()) { 2752 c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, " (", null))); 2753 c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(corePath+"terminologies.html#"+ved.getBinding().getStrength().toCode(), egt(ved.getBinding().getStrengthElement()), ved.getBinding().getStrength().getDefinition()))); 2754 c.getPieces().add(gen.new Piece(null, ")", null)); 2755 } 2756 if (ved.getBinding().hasDescription() && MarkDownProcessor.isSimpleMarkdown(ved.getBinding().getDescription())) { 2757 c.getPieces().add(gen.new Piece(null, ": ", null)); 2758 c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context.getWorker(), ved.getBinding().getDescriptionElement()).asStringValue()); 2759 } 2760 } 2761 c.addPiece(gen.new Piece("br")).addPiece(gen.new Piece(null, ProfileUtilities.describeExtensionContext(ed), null)); 2762 r.getCells().add(c); 2763 2764 try { 2765 return gen.generate(model, corePath, 0, outputTracker); 2766 } catch (org.hl7.fhir.exceptions.FHIRException e) { 2767 throw new FHIRException(e.getMessage(), e); 2768 } 2769 } 2770 2771 private String describeCardinality(ElementDefinition definition, ElementDefinition fallback, UnusedTracker tracker) { 2772 IntegerType min = definition.hasMinElement() ? definition.getMinElement() : new IntegerType(); 2773 StringType max = definition.hasMaxElement() ? definition.getMaxElement() : new StringType(); 2774 if (min.isEmpty() && fallback != null) 2775 min = fallback.getMinElement(); 2776 if (max.isEmpty() && fallback != null) 2777 max = fallback.getMaxElement(); 2778 2779 tracker.used = !max.isEmpty() && !max.getValue().equals("0"); 2780 2781 if (min.isEmpty() && max.isEmpty()) 2782 return null; 2783 else 2784 return (!min.hasValue() ? "" : Integer.toString(min.getValue())) + ".." + (!max.hasValue() ? "" : max.getValue()); 2785 } 2786 2787 2788 private ElementDefinition getValueFor(StructureDefinition ed, ElementDefinition c) { 2789 int i = ed.getSnapshot().getElement().indexOf(c) + 1; 2790 while (i < ed.getSnapshot().getElement().size() && ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".")) { 2791 if (ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".value")) 2792 return ed.getSnapshot().getElement().get(i); 2793 i++; 2794 } 2795 return null; 2796 } 2797 2798 2799 2800 private ElementDefinition getUrlFor(StructureDefinition ed, ElementDefinition c) { 2801 int i = ed.getSnapshot().getElement().indexOf(c) + 1; 2802 while (i < ed.getSnapshot().getElement().size() && ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".")) { 2803 if (ed.getSnapshot().getElement().get(i).getPath().equals(c.getPath()+".url")) 2804 return ed.getSnapshot().getElement().get(i); 2805 i++; 2806 } 2807 return null; 2808 } 2809 2810 2811 2812}