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