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