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