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