001package org.hl7.fhir.r5.renderers;
002
003import java.io.IOException;
004import java.io.UnsupportedEncodingException;
005import java.util.Collection;
006import java.util.HashMap;
007import java.util.HashSet;
008import java.util.List;
009import java.util.Map;
010import java.util.Set;
011
012import org.hl7.fhir.exceptions.FHIRException;
013import org.hl7.fhir.r5.model.CodeSystem;
014import org.hl7.fhir.r5.model.CodeType;
015import org.hl7.fhir.r5.model.ConceptMap;
016import org.hl7.fhir.r5.model.Enumeration;
017import org.hl7.fhir.r5.model.IdType;
018import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship;
019import org.hl7.fhir.r5.model.Enumerations.SearchComparator;
020import org.hl7.fhir.r5.model.Enumerations.SearchModifierCode;
021import org.hl7.fhir.r5.model.Enumerations.VersionIndependentResourceTypesAll;
022import org.hl7.fhir.r5.model.OperationDefinition;
023import org.hl7.fhir.r5.model.Resource;
024import org.hl7.fhir.r5.model.SearchParameter;
025import org.hl7.fhir.r5.model.SearchParameter.SearchParameterComponentComponent;
026import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupComponent;
027import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupInputComponent;
028import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupRuleComponent;
029import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupRuleDependentComponent;
030import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupRuleSourceComponent;
031import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupRuleTargetComponent;
032import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupRuleTargetParameterComponent;
033import org.hl7.fhir.r5.model.StructureMap.StructureMapStructureComponent;
034import org.hl7.fhir.r5.model.StructureMap.StructureMapTargetListMode;
035import org.hl7.fhir.r5.model.StructureMap.StructureMapTransform;
036import org.hl7.fhir.r5.model.StringType;
037import org.hl7.fhir.r5.model.StructureDefinition;
038import org.hl7.fhir.r5.model.StructureMap;
039import org.hl7.fhir.r5.model.UriType;
040import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
041import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent;
042import org.hl7.fhir.r5.model.DataType;
043import org.hl7.fhir.r5.renderers.utils.RenderingContext;
044import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType;
045import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
046import org.hl7.fhir.r5.utils.EOperationOutcome;
047import org.hl7.fhir.r5.utils.ToolingExtensions;
048import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
049import org.hl7.fhir.utilities.StandardsStatus;
050import org.hl7.fhir.utilities.Utilities;
051import org.hl7.fhir.utilities.VersionUtilities;
052import org.hl7.fhir.utilities.xhtml.XhtmlFluent;
053import org.hl7.fhir.utilities.xhtml.XhtmlNode;
054
055public class StructureMapRenderer extends TerminologyRenderer {
056
057  private static final String COLOR_COMMENT = "green";
058  private static final String COLOR_METADATA = "#cc00cc";
059  private static final String COLOR_CONST = "blue";
060  private static final String COLOR_VARIABLE = "maroon";
061  private static final String COLOR_SYNTAX = "navy";
062  private static final boolean RENDER_MULTIPLE_TARGETS_ONELINE = true;
063  private static final String COLOR_SPECIAL = "#b36b00";
064  private static final String DEFAULT_COMMENT = "This element was not defined prior to R5";
065  
066  private String clauseComment = DEFAULT_COMMENT;
067
068  public StructureMapRenderer(RenderingContext context) {
069    super(context);
070  }
071
072  public StructureMapRenderer(RenderingContext context, ResourceContext rcontext) {
073    super(context, rcontext);
074  }
075  
076  public boolean render(XhtmlNode x, Resource dr) throws IOException, FHIRException, EOperationOutcome {
077    return render(x, (StructureMap) dr);
078  }
079
080  public boolean render(XhtmlNode x, StructureMap map) throws IOException, FHIRException, EOperationOutcome {
081    renderMap(x.pre("fml"), map);
082    return false;
083  }
084
085  public void renderMap(XhtmlNode x, StructureMap map) {
086    x.tx("\r\n");
087    if (VersionUtilities.isR5Plus(context.getContext().getVersion())) {
088      renderMetadata(x, "url", map.getUrlElement());
089      renderMetadata(x, "name", map.getNameElement());
090      renderMetadata(x, "title", map.getTitleElement());
091      renderMetadata(x, "status", map.getStatusElement());      
092      x.tx("\r\n");
093    } else {
094      x.b().tx("map");
095      x.color(COLOR_SYNTAX).tx(" \"");
096      x.tx(map.getUrl());
097      x.color(COLOR_SYNTAX).tx("\" = \"");
098      x.tx(Utilities.escapeJson(map.getName()));
099      x.color(COLOR_SYNTAX).tx("\"\r\n\r\n");
100      if (map.getDescription() != null) {
101        renderMultilineDoco(x, map.getDescription(), 0, null);
102        x.tx("\r\n");
103      }
104    }
105    renderConceptMaps(x, map);
106    renderUses(x, map);
107    renderImports(x, map);
108    for (StructureMapGroupComponent g : map.getGroup())
109      renderGroup(x, g);
110  }
111
112  private void renderMetadata(XhtmlNode x, String name, DataType value) {
113    if (!value.isEmpty()) {
114      renderMetadata(x, name, value, null);
115    }
116  }
117  
118  private void renderMetadata(XhtmlNode x, String name, DataType value, String def) {
119    String v = value.primitiveValue();
120    if (def == null || !def.equals(v)) {
121      XhtmlNode c = x.color(COLOR_METADATA);
122      c.tx("/// ");
123      c.b().tx(name);
124      c.tx(" = ");
125      if (Utilities.existsInList(v, "true", "false") || Utilities.isDecimal(v, true)) {
126        x.color(COLOR_CONST).tx(v);
127      } else {
128        x.color(COLOR_CONST).tx("'"+v+"'");
129      }
130      x.tx("\r\n");
131    }
132  }
133
134  private void renderConceptMaps(XhtmlNode x,StructureMap map) {
135    for (Resource r : map.getContained()) {
136      if (r instanceof ConceptMap) {
137        produceConceptMap(x, (ConceptMap) r);
138      }
139    }
140  }
141
142  private void produceConceptMap(XhtmlNode x,ConceptMap cm) {
143    if (cm.hasFormatCommentPre()) {
144      renderMultilineDoco(x, cm.getFormatCommentsPre(), 0, null);
145    }
146    x.b().tx("conceptmap");
147    x.color(COLOR_SYNTAX).tx(" \"");
148    x.tx(cm.getId());
149    x.color(COLOR_SYNTAX).tx("\" {\r\n");
150    Map<String, String> prefixesSrc = new HashMap<String, String>();
151    Map<String, String> prefixesTgt = new HashMap<String, String>();
152    char prefix = 's';
153    for (ConceptMapGroupComponent cg : cm.getGroup()) {
154      if (!prefixesSrc.containsKey(cg.getSource())) {
155        prefixesSrc.put(cg.getSource(), String.valueOf(prefix));
156        x.b().tx("  prefix ");
157        x.tx(""+prefix);
158        x.color(COLOR_SYNTAX).tx(" = \"");
159        CodeSystem cs = context.getContext().fetchResource(CodeSystem.class, cg.getSource());
160        if (cs != null && cs.hasWebPath()) {
161          x.ah(cs.getWebPath(), cs.present()).tx(cg.getSource());
162        } else {
163          x.tx(cg.getSource());
164        }
165        x.color(COLOR_SYNTAX).tx("\"\r\n");
166        prefix++;
167      }
168      if (!prefixesTgt.containsKey(cg.getTarget())) {
169        prefixesTgt.put(cg.getTarget(), String.valueOf(prefix));
170        x.b().tx("  prefix ");
171        x.tx(""+prefix);
172        x.color(COLOR_SYNTAX).tx(" = \"");
173        CodeSystem cs = context.getContext().fetchResource(CodeSystem.class, cg.getTarget());
174        if (cs != null && cs.hasWebPath()) {
175          x.ah(cs.getWebPath(), cs.present()).tx(cg.getTarget());
176        } else {
177          x.tx(""+cg.getTarget());
178        }
179        x.color(COLOR_SYNTAX).tx("\"\r\n");
180        prefix++;
181      }
182    }
183    x.tx("\r\n");
184    for (ConceptMapGroupComponent cg : cm.getGroup()) {
185      if (cg.hasUnmapped()) {
186        x.b().tx("  unmapped for ");
187        x.tx(prefixesSrc.get(cg.getSource()));
188        x.color(COLOR_SYNTAX).tx(" = ");
189        x.tx(cg.getUnmapped().getMode().toCode());
190        x.tx("\r\n");
191      }
192    }
193
194    for (ConceptMapGroupComponent cg : cm.getGroup()) {
195      if (cg.hasFormatCommentPre()) {
196        renderMultilineDoco(x, cg.getFormatCommentsPre(), 2, prefixesSrc.values());
197      }
198      for (SourceElementComponent ce : cg.getElement()) {
199        if (ce.hasFormatCommentPre()) {
200          renderMultilineDoco(x, ce.getFormatCommentsPre(), 2, prefixesSrc.values());
201        }
202
203        x.tx("  ");
204        x.tx(prefixesSrc.get(cg.getSource()));
205        x.color(COLOR_SYNTAX).tx(":");
206        if (Utilities.isToken(ce.getCode())) {
207          x.tx(ce.getCode());
208        } else {
209          x.tx("\"");
210          x.tx(ce.getCode());
211          x.tx("\"");
212        }
213        x.tx(" ");
214        x.b().tx(getChar(ce.getTargetFirstRep().getRelationship()));
215        x.tx(" ");
216        x.tx(prefixesTgt.get(cg.getTarget()));
217        x.color(COLOR_SYNTAX).tx(":");
218        if (Utilities.isToken(ce.getTargetFirstRep().getCode())) {
219          x.tx(ce.getTargetFirstRep().getCode());
220        } else {
221          x.color(COLOR_SYNTAX).tx("\"");
222          x.tx(ce.getTargetFirstRep().getCode());
223          x.color(COLOR_SYNTAX).tx("\"");
224        }
225        x.tx("\r\n");
226        if (ce.hasFormatCommentPost()) {
227          renderMultilineDoco(x, ce.getFormatCommentsPost(), 2, prefixesSrc.values());
228        }
229      }
230      if (cg.hasFormatCommentPost()) {
231        renderMultilineDoco(x, cg.getFormatCommentsPost(), 2, prefixesSrc.values());
232      }
233    }
234    if (cm.hasFormatCommentPost()) {
235      renderMultilineDoco(x, cm.getFormatCommentsPost(), 2, prefixesSrc.values());
236    }
237    x.color(COLOR_SYNTAX).tx("}\r\n\r\n");
238  }
239
240  private String getChar(ConceptMapRelationship relationship) {
241    switch (relationship) {
242      case RELATEDTO:
243        return "-";
244      case EQUIVALENT:
245        return "==";
246      case NOTRELATEDTO:
247        return "!=";
248      case SOURCEISNARROWERTHANTARGET:
249        return "<=";
250      case SOURCEISBROADERTHANTARGET:
251        return ">=";
252      default:
253        return "??";
254    }
255  }
256
257  private void renderUses(XhtmlNode x,StructureMap map) {
258    for (StructureMapStructureComponent s : map.getStructure()) {
259      x.b().tx("uses");
260      x.color(COLOR_SYNTAX).tx(" \"");
261      StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, s.getUrl());
262      if (sd != null && sd.hasWebPath()) {
263        x.ah(sd.getWebPath(), sd.present()).tx(s.getUrl());
264      } else {
265        x.tx(s.getUrl());
266      }
267      x.color(COLOR_SYNTAX).tx("\" ");
268      if (s.hasAlias()) {
269        x.b().tx("alias ");
270        x.tx(s.getAlias());
271        x.tx(" ");
272      }
273      x.b().tx("as ");
274      x.b().tx(s.getMode().toCode());
275      renderDoco(x, s.getDocumentation(), false, null);
276      x.tx("\r\n");
277    }
278    if (map.hasStructure())
279      x.tx("\r\n");
280  }
281
282  private void renderImports(XhtmlNode x,StructureMap map) {
283    for (UriType s : map.getImport()) {
284      x.b().tx("imports");
285      x.color(COLOR_SYNTAX).tx(" \"");
286      StructureMap m = context.getContext().fetchResource(StructureMap.class, s.getValue());
287      if (m != null) {
288        x.ah(m.getWebPath(), m.present()).tx(s.getValue());
289      } else {
290        x.tx(s.getValue());
291      }
292      x.color(COLOR_SYNTAX).tx("\"\r\n");
293    }
294    if (map.hasImport())
295      x.tx("\r\n");
296  }
297
298  private void renderGroup(XhtmlNode x,StructureMapGroupComponent g) {
299    Collection<String> tokens = scanVariables(g, null);
300    if (g.hasFormatCommentPre()) {
301      renderMultilineDoco(x, g.getFormatCommentsPre(), 0, tokens);
302    }
303    if (g.hasDocumentation()) {
304      renderMultilineDoco(x, g.getDocumentation(), 0, tokens);
305    }
306    x.b().tx("group ");
307    x.tx(g.getName());
308    x.color(COLOR_SYNTAX).tx("(");
309    boolean first = true;
310    for (StructureMapGroupInputComponent gi : g.getInput()) {
311      if (first)
312        first = false;
313      else
314        x.tx(", ");
315      x.b().tx(gi.getMode().toCode());
316      x.tx(" ");
317      x.color(COLOR_VARIABLE).tx(gi.getName());
318      if (gi.hasType()) {
319        x.color(COLOR_SYNTAX).tx(" : ");
320        x.tx(gi.getType());
321      }
322    }
323    x.color(COLOR_SYNTAX).tx(")");
324    if (g.hasExtends()) {
325      x.b().tx(" extends ");
326      String ref = resolveRuleReference(g.getExtendsElement());
327      if (ref != null) {
328        x.ah(ref).tx(g.getExtends()); 
329      } else {
330        x.tx(g.getExtends());
331      }
332    }
333
334    if (g.hasTypeMode()) {
335      switch (g.getTypeMode()) {
336        case TYPES:
337          x.b().tx(" <<types>>");
338          break;
339        case TYPEANDTYPES:
340          x.b().tx(" <<type+>>");
341          break;
342        default: // NONE, NULL
343      }
344    }
345    x.color(COLOR_SYNTAX).tx(" {\r\n");
346    for (StructureMapGroupRuleComponent r : g.getRule()) {
347      renderRule(x, g, r, 2);
348    }
349    if (g.hasFormatCommentPost()) {
350      renderMultilineDoco(x, g.getFormatCommentsPost(), 0, scanVariables(g, null));
351    }
352    x.color(COLOR_SYNTAX).tx("}\r\n\r\n");
353  }
354
355  private String resolveRuleReference(IdType idType) {
356    return null;
357  }
358
359  private void renderRule(XhtmlNode x, StructureMapGroupComponent g, StructureMapGroupRuleComponent r, int indent) {
360    Collection<String> tokens = scanVariables(g, r);
361    if (r.hasFormatCommentPre()) {
362      renderMultilineDoco(x, r.getFormatCommentsPre(), indent, tokens);
363    }
364    for (int i = 0; i < indent; i++)
365      x.tx(" ");
366    boolean canBeAbbreviated = checkisSimple(r);
367    {
368      boolean first = true;
369      for (StructureMapGroupRuleSourceComponent rs : r.getSource()) {
370        if (first)
371          first = false;
372        else
373          x.color(COLOR_SYNTAX).tx(", ");
374        renderSource(x, rs, canBeAbbreviated);
375      }
376    }
377    if (r.getTarget().size() > 1) {
378      x.color(COLOR_SYNTAX).b().tx(" -> ");
379      boolean first = true;
380      for (StructureMapGroupRuleTargetComponent rt : r.getTarget()) {
381        if (first)
382          first = false;
383        else
384          x.color(COLOR_SYNTAX).tx(", ");
385        if (RENDER_MULTIPLE_TARGETS_ONELINE)
386          x.tx(" ");
387        else {
388          x.tx("\r\n");
389          for (int i = 0; i < indent + 4; i++)
390            x.tx(" ");
391        }
392        renderTarget(x, rt, false);
393      }
394    } else if (r.hasTarget()) {
395      x.color(COLOR_SYNTAX).b().tx(" -> ");
396      renderTarget(x, r.getTarget().get(0), canBeAbbreviated);
397    }
398    if (r.hasRule()) {
399      x.b().tx(" then");
400      x.color(COLOR_SYNTAX).tx(" {\r\n");
401      for (StructureMapGroupRuleComponent ir : r.getRule()) {
402        renderRule(x, g, ir, indent + 2);
403      }
404      for (int i = 0; i < indent; i++)
405        x.tx(" ");
406      x.color(COLOR_SYNTAX).tx("}");
407    } else {
408      if (r.hasDependent() && !canBeAbbreviated) {
409        x.b().tx(" then ");
410        boolean first = true;
411        for (StructureMapGroupRuleDependentComponent rd : r.getDependent()) {
412          if (first)
413            first = false;
414          else
415            x.color(COLOR_SYNTAX).tx(", ");
416          String ref = resolveRuleReference(rd.getNameElement());
417          if (ref != null) {
418            x.ah(ref).tx(rd.getName());
419          } else {
420            x.tx(rd.getName());
421          }
422          x.color(COLOR_SYNTAX).tx("(");
423          boolean ifirst = true;
424          for (StructureMapGroupRuleTargetParameterComponent rdp : rd.getParameter()) {
425            if (ifirst)
426              ifirst = false;
427            else
428              x.color(COLOR_SYNTAX).tx(", ");
429            renderTransformParam(x, rdp);
430          }
431          x.color(COLOR_SYNTAX).tx(")");
432        }
433      }
434    }
435    if (r.hasName()) {
436      String n = ntail(r.getName());
437      if (!n.startsWith("\""))
438        n = "\"" + n + "\"";
439      if (!matchesName(n, r.getSource())) {
440        x.tx(" ");
441        x.i().tx(n);
442      }
443    }
444    x.color(COLOR_SYNTAX).tx(";");
445    if (r.hasDocumentation()) {
446      renderDoco(x, r.getDocumentation(), false, null);
447    }
448    x.tx("\r\n");
449    if (r.hasFormatCommentPost()) {
450      renderMultilineDoco(x, r.getFormatCommentsPost(), indent, tokens);
451    }
452  }
453
454  private Collection<String> scanVariables(StructureMapGroupComponent g, StructureMapGroupRuleComponent r) {
455    Set<String> res = new HashSet<>();
456    for (StructureMapGroupInputComponent input : g.getInput()) {
457      res.add(input.getName());
458    }
459    if (r != null) {
460      for (StructureMapGroupRuleSourceComponent src : r.getSource()) {
461        if (src.hasVariable()) {
462          res.add(src.getVariable());
463        }
464      }
465    }
466    return res;
467  }
468
469  private boolean matchesName(String n, List<StructureMapGroupRuleSourceComponent> source) {
470    if (source.size() != 1)
471      return false;
472    if (!source.get(0).hasElement())
473      return false;
474    String s = source.get(0).getElement();
475    if (n.equals(s) || n.equals("\"" + s + "\""))
476      return true;
477    if (source.get(0).hasType()) {
478      s = source.get(0).getElement() + Utilities.capitalize(source.get(0).getType());
479      return n.equals(s) || n.equals("\"" + s + "\"");
480    }
481    return false;
482  }
483
484  private String ntail(String name) {
485    if (name == null)
486      return null;
487    if (name.startsWith("\"")) {
488      name = name.substring(1);
489      name = name.substring(0, name.length() - 1);
490    }
491    return "\"" + (name.contains(".") ? name.substring(name.lastIndexOf(".") + 1) : name) + "\"";
492  }
493
494  private boolean checkisSimple(StructureMapGroupRuleComponent r) {
495    return
496      (r.getSource().size() == 1 && r.getSourceFirstRep().hasElement() && r.getSourceFirstRep().hasVariable()) &&
497        (r.getTarget().size() == 1 && r.getTargetFirstRep().hasVariable() && (r.getTargetFirstRep().getTransform() == null || r.getTargetFirstRep().getTransform() == StructureMapTransform.CREATE) && r.getTargetFirstRep().getParameter().size() == 0) &&
498        (r.getDependent().size() == 0 || (r.getDependent().size() == 1 && StructureMapUtilities.DEF_GROUP_NAME.equals(r.getDependentFirstRep().getName()))) && (r.getRule().size() == 0);
499  }
500  
501  private void renderSource(XhtmlNode x,StructureMapGroupRuleSourceComponent rs, boolean abbreviate) {
502    x.tx(rs.getContext());
503    if (rs.getContext().equals("@search")) {
504      x.color(COLOR_SYNTAX).tx("(");
505      x.tx(rs.getElement());
506      x.color(COLOR_SYNTAX).tx(")");
507    } else if (rs.hasElement()) {
508      x.tx(".");
509      x.tx(rs.getElement());
510    }
511    if (rs.hasType()) {
512      x.color(COLOR_SYNTAX).tx(" : ");
513      x.tx(rs.getType());
514      if (rs.hasMin()) {
515        x.tx(" ");
516        x.tx(rs.getMin());
517        x.color(COLOR_SYNTAX).tx("..");
518        x.tx(rs.getMax());
519      }
520    }
521
522    if (rs.hasListMode()) {
523      x.tx(" ");
524      x.tx(rs.getListMode().toCode());
525    }
526    if (rs.hasDefaultValue()) {
527      x.b().tx(" default ");
528      x.tx("\"" + Utilities.escapeJson(rs.getDefaultValue()) + "\"");
529    }
530    if (!abbreviate && rs.hasVariable()) {
531      x.b().tx(" as ");
532      x.color(COLOR_VARIABLE).tx(rs.getVariable());
533    }
534    if (rs.hasCondition()) {
535      x.b().tx(" where ");
536      x.tx(rs.getCondition());
537    }
538    if (rs.hasCheck()) {
539      x.b().tx(" check ");
540      x.tx(rs.getCheck());
541    }
542    if (rs.hasLogMessage()) {
543      x.b().tx(" log ");
544      x.tx(rs.getLogMessage());
545    }
546  }
547  
548  private void renderTarget(XhtmlNode x,StructureMapGroupRuleTargetComponent rt, boolean abbreviate) {
549    if (rt.hasContext()) {
550      x.tx(rt.getContext());
551      if (rt.hasElement()) {
552        x.tx(".");
553        x.tx(rt.getElement());
554      }
555    }
556    if (!abbreviate && rt.hasTransform()) {
557      if (rt.hasContext())
558        x.tx(" = ");
559      if (rt.getTransform() == StructureMapTransform.COPY && rt.getParameter().size() == 1) {
560        renderTransformParam(x, rt.getParameter().get(0));
561      } else if (rt.getTransform() == StructureMapTransform.EVALUATE && rt.getParameter().size() == 1) {
562        x.color(COLOR_SYNTAX).tx("(");
563        x.tx(((StringType) rt.getParameter().get(0).getValue()).asStringValue());
564        x.color(COLOR_SYNTAX).tx(")");
565      } else if (rt.getTransform() == StructureMapTransform.EVALUATE && rt.getParameter().size() == 2) {
566        x.tx(rt.getTransform().toCode());
567        x.color(COLOR_SYNTAX).tx("(");
568        x.tx(((IdType) rt.getParameter().get(0).getValue()).asStringValue());
569        x.color(COLOR_SYNTAX).tx(", ");
570        x.tx(((StringType) rt.getParameter().get(1).getValue()).asStringValue());
571        x.color(COLOR_SYNTAX).tx(")");
572      } else {
573        x.b().tx(rt.getTransform().toCode());
574        x.color(COLOR_SYNTAX).tx("(");
575        boolean first = true;
576        for (StructureMapGroupRuleTargetParameterComponent rtp : rt.getParameter()) {
577          if (first)
578            first = false;
579          else
580            x.color(COLOR_SYNTAX).tx(", ");
581          renderTransformParam(x, rtp);
582        }
583        x.color(COLOR_SYNTAX).tx(")");
584      }
585    }
586    if (!abbreviate && rt.hasVariable()) {
587      x.b().tx(" as ");
588      x.color(COLOR_VARIABLE).tx(rt.getVariable());
589    }
590    for (Enumeration<StructureMapTargetListMode> lm : rt.getListMode()) {
591      x.tx(" ");
592      x.b().tx(lm.getValue().toCode());
593      if (lm.getValue() == StructureMapTargetListMode.SHARE) {
594        x.tx(" ");
595        x.b().tx(rt.getListRuleId());
596      }
597    }
598  }
599
600
601  private void renderTransformParam(XhtmlNode x,StructureMapGroupRuleTargetParameterComponent rtp) {
602    try {
603      if (rtp.hasValueBooleanType())
604        x.color(COLOR_CONST).tx(rtp.getValueBooleanType().asStringValue());
605      else if (rtp.hasValueDecimalType())
606        x.color(COLOR_CONST).tx(rtp.getValueDecimalType().asStringValue());
607      else if (rtp.hasValueIdType())
608        x.color(COLOR_VARIABLE).tx(rtp.getValueIdType().asStringValue());
609      else if (rtp.hasValueIntegerType())
610        x.color(COLOR_CONST).tx(rtp.getValueIntegerType().asStringValue());
611      else
612        x.color(COLOR_CONST).tx("'" + Utilities.escapeJava(rtp.getValueStringType().asStringValue()) + "'");
613    } catch (FHIRException e) {
614      e.printStackTrace();
615      x.tx("error!");
616    }
617  }
618
619  private void renderDoco(XhtmlNode x,String doco, boolean startLine, Collection<String> tokens) {
620    if (Utilities.noString(doco))
621      return;
622    if (!startLine) {
623      x.tx(" ");
624    }
625    boolean isClause = false;
626    String t = doco.trim().replace(" ", "");
627    if (tokens != null) {
628      for (String s : tokens) {
629        if (t.startsWith(s+":") || t.startsWith(s+".") || t.startsWith(s+"->")) {
630          isClause = true;
631          break;
632        }
633      }
634    }
635    if (isClause) {
636      XhtmlNode s= x.color(COLOR_SPECIAL);
637      s.setAttribute("title", clauseComment );
638      s.tx("// ");
639      s.tx(doco.replace("\r\n", " ").replace("\r", " ").replace("\n", " "));      
640    } else {
641      x.color(COLOR_SYNTAX).tx("// ");
642      x.color(COLOR_COMMENT).tx(doco.replace("\r\n", " ").replace("\r", " ").replace("\n", " "));
643    }
644  }
645
646  private void renderMultilineDoco(XhtmlNode x,String doco, int indent, Collection<String> tokens) {
647    if (Utilities.noString(doco))
648      return;
649    String[] lines = doco.split("\\r?\\n");
650    for (String line : lines) {
651      for (int i = 0; i < indent; i++)
652        x.tx(" ");
653      renderDoco(x, line, true, tokens);
654      x.tx("\r\n");
655    }
656  }
657
658  private void renderMultilineDoco(XhtmlNode x, List<String> doco, int indent, Collection<String> tokens) {
659    for (String line : doco) {
660      for (int i = 0; i < indent; i++)
661        x.tx(" ");
662      renderDoco(x, line, true, tokens);
663      x.tx("\r\n");
664    }
665  }
666
667
668  public void describe(XhtmlNode x, OperationDefinition opd) {
669    x.tx(display(opd));
670  }
671
672  public String display(OperationDefinition opd) {
673    return opd.present();
674  }
675
676  @Override
677  public String display(Resource r) throws UnsupportedEncodingException, IOException {
678    return ((StructureMap) r).present();
679  }
680
681}