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