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