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