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