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