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