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