001package org.hl7.fhir.r5.elementmodel;
002
003import java.io.ByteArrayInputStream;
004import java.io.IOException;
005import java.io.InputStream;
006import java.io.OutputStream;
007import java.util.ArrayList;
008import java.util.HashMap;
009import java.util.List;
010import java.util.Map;
011
012import org.hl7.fhir.exceptions.DefinitionException;
013import org.hl7.fhir.exceptions.FHIRException;
014import org.hl7.fhir.exceptions.FHIRFormatError;
015import org.hl7.fhir.r5.context.IWorkerContext;
016import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
017import org.hl7.fhir.r5.fhirpath.ExpressionNode;
018import org.hl7.fhir.r5.fhirpath.FHIRLexer;
019import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
020import org.hl7.fhir.r5.fhirpath.FHIRLexer.FHIRLexerException;
021import org.hl7.fhir.r5.formats.IParser.OutputStyle;
022import org.hl7.fhir.r5.model.StructureDefinition;
023import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupUnmappedMode;
024import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship;
025import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
026import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupTypeMode;
027import org.hl7.fhir.r5.model.StructureMap.StructureMapTransform;
028import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
029import org.hl7.fhir.utilities.SourceLocation;
030import org.hl7.fhir.utilities.TextFile;
031import org.hl7.fhir.utilities.Utilities;
032import org.hl7.fhir.utilities.VersionUtilities;
033import org.hl7.fhir.utilities.validation.ValidationMessage;
034import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
035import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
036import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
037
038public class FmlParser extends ParserBase {
039
040  private FHIRPathEngine fpe;
041
042  public FmlParser(IWorkerContext context) {
043    super(context);
044    fpe = new FHIRPathEngine(context);
045  }
046
047  @Override
048  public List<ValidatedFragment> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
049    byte[] content = TextFile.streamToBytes(inStream);
050    ByteArrayInputStream stream = new ByteArrayInputStream(content);
051    String text = TextFile.streamToString(stream);
052    List<ValidatedFragment> result = new ArrayList<>();
053    ValidatedFragment focusFragment = new ValidatedFragment(ValidatedFragment.FOCUS_NAME, "fml", content, false);
054    focusFragment.setElement(parse(focusFragment.getErrors(), text));
055    result.add(focusFragment);
056    return result;
057  }
058
059  @Override
060  public void compose(Element e, OutputStream destination, OutputStyle style, String base)
061      throws FHIRException, IOException {
062    throw new Error("Not done yet");
063  }
064
065  public Element parse(List<ValidationMessage> errors, String text) throws FHIRException {
066    FHIRLexer lexer = new FHIRLexer(text, "source", true, true);
067    if (lexer.done())
068      throw lexer.error("Map Input cannot be empty");
069    Element result = Manager.build(context, context.fetchTypeDefinition("StructureMap"));
070    try {
071      if (lexer.hasToken("map")) {
072        lexer.token("map");
073        result.makeElement("url").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("url"));
074        lexer.token("=");
075        result.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("name"));
076        if (lexer.hasComments()) {
077          result.makeElement("description").markLocation(lexer.getCurrentLocation()).setValue(lexer.getAllComments());
078        }
079      }
080      while (lexer.hasToken("///")) {
081        lexer.next();
082        String fid = lexer.takeDottedToken();
083        Element e = result.makeElement(fid).markLocation(lexer.getCurrentLocation());
084        lexer.token("=");
085        e.setValue(lexer.readConstant("meta value"));
086      }
087      lexer.setMetadataFormat(false);
088      if (!result.hasChild("status")) {
089        result.makeElement("status").setValue("draft");
090      }
091      if (!result.hasChild("id") && result.hasChild("name")) {
092        String id = Utilities.makeId(result.getChildValue("name"));
093        if (!Utilities.noString(id)) {
094          result.makeElement("id").setValue(id);
095        }
096      }
097      if (!result.hasChild("description") && result.hasChild("title")) {
098        result.makeElement("description").setValue(Utilities.makeId(result.getChildValue("title")));
099      }
100      
101      while (lexer.hasToken("conceptmap"))
102        parseConceptMap(result, lexer);
103
104      while (lexer.hasToken("uses"))
105        parseUses(result, lexer);
106      while (lexer.hasToken("imports"))
107        parseImports(result, lexer);
108
109      while (lexer.hasToken("conceptmap"))
110        parseConceptMap(result, lexer);
111
112      while (!lexer.done()) {
113        parseGroup(result, lexer);
114      }
115    } catch (FHIRLexerException e) {
116      if (policy == ValidationPolicy.NONE) {
117        throw e;
118      } else {
119        logError(errors, "2023-02-24", e.getLocation().getLine(), e.getLocation().getColumn(), "??", IssueType.INVALID, e.getMessage(), IssueSeverity.FATAL);
120      }
121    } catch (Exception e) {
122      if (policy == ValidationPolicy.NONE) {
123        throw e;
124      } else {
125        logError(errors, "2023-02-24", -1, -1, "?", IssueType.INVALID, e.getMessage(), IssueSeverity.FATAL);
126      }
127    }
128    result.setIgnorePropertyOrder(true);
129    return result;
130  }
131
132  private void parseConceptMap(Element structureMap, FHIRLexer lexer) throws FHIRLexerException {
133    lexer.token("conceptmap");
134    Element map = structureMap.makeElement("contained");
135    StructureDefinition sd = context.fetchTypeDefinition("ConceptMap");
136    map.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(map.getElementProperty() != null ? map.getElementProperty() : map.getProperty()), map.getProperty());
137    map.setType("ConceptMap");
138    Element eid = map.makeElement("id").markLocation(lexer.getCurrentLocation());
139    String id = lexer.readConstant("map id");
140    if (id.startsWith("#"))
141      throw lexer.error("Concept Map identifier must not start with #");
142    eid.setValue(id);
143    map.makeElement("status").setValue(structureMap.getChildValue("status"));
144    lexer.token("{");
145    //    lexer.token("source");
146    //    map.setSource(new UriType(lexer.readConstant("source")));
147    //    lexer.token("target");
148    //    map.setSource(new UriType(lexer.readConstant("target")));
149    Map<String, String> prefixes = new HashMap<String, String>();
150    while (lexer.hasToken("prefix")) {
151      lexer.token("prefix");
152      String n = lexer.take();
153      lexer.token("=");
154      String v = lexer.readConstant("prefix url");
155      prefixes.put(n, v);
156    }
157    while (lexer.hasToken("unmapped")) {
158      lexer.token("unmapped");
159      lexer.token("for");
160      String n = readPrefix(prefixes, lexer);
161      Element g = getGroupE(map, n, null);
162      lexer.token("=");
163      SourceLocation loc = lexer.getCurrentLocation();
164      String v = lexer.take();
165      if (v.equals("provided")) {
166        g.makeElement("unmapped").makeElement("mode").markLocation(loc).setValue(ConceptMapGroupUnmappedMode.USESOURCECODE.toCode());
167      } else
168        throw lexer.error("Only unmapped mode PROVIDED is supported at this time");
169    }
170    while (!lexer.hasToken("}")) {
171      String comments = lexer.hasComments() ? lexer.getAllComments() : null;
172      String srcs = readPrefix(prefixes, lexer);
173      lexer.token(":");
174      SourceLocation scloc = lexer.getCurrentLocation();
175      String sc = lexer.getCurrent().startsWith("\"") ? lexer.readConstant("code") : lexer.take();
176      SourceLocation relLoc = lexer.getCurrentLocation();
177      ConceptMapRelationship rel = readRelationship(lexer);
178      String tgts = readPrefix(prefixes, lexer);
179      Element g = getGroupE(map, srcs, tgts);
180      Element e = g.addElement("element");
181      if (comments != null) {
182        for (String s : comments.split("\\r\\n")) {
183          e.getComments().add(s);
184        }
185      }
186      e.makeElement("code").markLocation(scloc).setValue(sc.startsWith("\"") ? lexer.processConstant(sc) : sc);
187      Element tgt = e.addElement("target");
188      tgt.makeElement("relationship").markLocation(relLoc).setValue(rel.toCode());
189      lexer.token(":");
190      tgt.makeElement("code").markLocation(lexer.getCurrentLocation()).setValue(lexer.getCurrent().startsWith("\"") ? lexer.readConstant("code") : lexer.take());
191      if (lexer.hasComments()) {
192        tgt.makeElement("comment").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
193      }
194    }
195    lexer.token("}");
196  }
197  
198  private Element getGroupE(Element map, String srcs, String tgts) {
199    for (Element grp : map.getChildrenByName("group")) {
200      if (grp.getChildValue("source").equals(srcs)) {
201        Element tgt = grp.getNamedChild("target");
202        if (tgt == null || tgts == null || tgts.equals(tgt.getValue())) {
203          if (tgt == null && tgts != null)
204            grp.makeElement("target").setValue(tgts);
205          return grp;
206        }
207      }
208    }
209    Element grp = map.addElement("group");
210    grp.makeElement("source").setValue(srcs);
211    grp.makeElement("target").setValue(tgts);
212    return grp;
213  }
214
215  private String readPrefix(Map<String, String> prefixes, FHIRLexer lexer) throws FHIRLexerException {
216    String prefix = lexer.take();
217    if (!prefixes.containsKey(prefix))
218      throw lexer.error("Unknown prefix '" + prefix + "'");
219    return prefixes.get(prefix);
220  }
221
222
223  private ConceptMapRelationship readRelationship(FHIRLexer lexer) throws FHIRLexerException {
224    String token = lexer.take();
225    if (token.equals("-"))
226      return ConceptMapRelationship.RELATEDTO;
227    if (token.equals("=")) // temporary
228      return ConceptMapRelationship.RELATEDTO;
229    if (token.equals("=="))
230      return ConceptMapRelationship.EQUIVALENT;
231    if (token.equals("!="))
232      return ConceptMapRelationship.NOTRELATEDTO;
233    if (token.equals("<="))
234      return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET;
235    if (token.equals(">="))
236      return ConceptMapRelationship.SOURCEISBROADERTHANTARGET;
237    throw lexer.error("Unknown relationship token '" + token + "'");
238  }
239
240  private void parseUses(Element result, FHIRLexer lexer) throws FHIRException {
241    lexer.token("uses");
242    Element st = result.addElement("structure");
243    st.makeElement("url").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("url"));
244    if (lexer.hasToken("alias")) {
245      lexer.token("alias");
246      st.makeElement("alias").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
247    }
248    lexer.token("as");
249    st.makeElement("mode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
250    lexer.skipToken(";");
251    if (lexer.hasComments()) {
252      st.makeElement("documentation").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
253    }
254  }
255  
256
257  private void parseImports(Element result, FHIRLexer lexer) throws FHIRException {
258    lexer.token("imports");
259    result.addElement("import").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("url"));
260    lexer.skipToken(";");
261  }
262
263  private void parseGroup(Element result, FHIRLexer lexer) throws FHIRException {
264    SourceLocation commLoc = lexer.getCommentLocation();
265    String comment = lexer.getAllComments();
266    lexer.token("group");
267    Element group = result.addElement("group").markLocation(lexer.getCurrentLocation());
268    if (!Utilities.noString(comment)) {
269      group.makeElement("documentation").markLocation(commLoc).setValue(comment);
270    }
271    boolean newFmt = false;
272    if (lexer.hasToken("for")) {
273      lexer.token("for");
274      SourceLocation loc = lexer.getCurrentLocation();
275      if ("type".equals(lexer.getCurrent())) {
276        lexer.token("type");
277        lexer.token("+");
278        lexer.token("types");
279        group.makeElement("typeMode").markLocation(loc).setValue(StructureMapGroupTypeMode.TYPEANDTYPES.toCode());
280      } else {
281        lexer.token("types");
282        group.makeElement("typeMode").markLocation(loc).setValue(StructureMapGroupTypeMode.TYPES.toCode());
283      }
284    }
285    group.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
286    if (lexer.hasToken("(")) {
287      newFmt = true;
288      lexer.take();
289      while (!lexer.hasToken(")")) {
290        parseInput(group, lexer, true);
291        if (lexer.hasToken(","))
292          lexer.token(",");
293      }
294      lexer.take();
295    }
296    if (lexer.hasToken("extends")) {
297      lexer.next();
298      group.makeElement("extends").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
299    }
300    if (newFmt) {
301      if (lexer.hasToken("<")) {
302        lexer.token("<");
303        lexer.token("<");
304        if (lexer.hasToken("types")) {
305          group.makeElement("typeMode").markLocation(lexer.getCurrentLocation()).setValue(StructureMapGroupTypeMode.TYPES.toCode());
306          lexer.token("types");
307        } else {
308          group.makeElement("typeMode").markLocation(lexer.getCurrentLocation()).setValue(StructureMapGroupTypeMode.TYPEANDTYPES.toCode());
309          lexer.token("type");
310          lexer.token("+");
311        }
312        lexer.token(">");
313        lexer.token(">");
314      }
315      lexer.token("{");
316    }
317    if (newFmt) {
318      while (!lexer.hasToken("}")) {
319        if (lexer.done())
320          throw lexer.error("premature termination expecting 'endgroup'");
321        parseRule(result, group, lexer, true);
322      }
323    } else {
324      while (lexer.hasToken("input"))
325        parseInput(group, lexer, false);
326      while (!lexer.hasToken("endgroup")) {
327        if (lexer.done())
328          throw lexer.error("premature termination expecting 'endgroup'");
329        parseRule(result, group, lexer, false);
330      }
331    }
332    lexer.next();
333    if (newFmt && lexer.hasToken(";"))
334      lexer.next();
335  }
336  
337
338  private void parseRule(Element map, Element context, FHIRLexer lexer, boolean newFmt) throws FHIRException {
339    Element rule = context.addElement("rule").markLocation(lexer.getCurrentLocation());
340    if (!newFmt) {
341      rule.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.takeDottedToken());
342      lexer.token(":");
343      lexer.token("for");
344    } else {
345      if (lexer.hasComments()) {
346        rule.makeElement("documentation").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
347      }
348    }
349
350    boolean done = false;
351    while (!done) {
352      parseSource(rule, lexer);
353      done = !lexer.hasToken(",");
354      if (!done)
355        lexer.next();
356    }
357    if ((newFmt && lexer.hasToken("->")) || (!newFmt && lexer.hasToken("make"))) {
358      lexer.token(newFmt ? "->" : "make");
359      done = false;
360      while (!done) {
361        parseTarget(rule, lexer);
362        done = !lexer.hasToken(",");
363        if (!done)
364          lexer.next();
365      }
366    }
367    if (lexer.hasToken("then")) {
368      lexer.token("then");
369      if (lexer.hasToken("{")) {
370        lexer.token("{");
371        while (!lexer.hasToken("}")) {
372          if (lexer.done())
373            throw lexer.error("premature termination expecting '}' in nested group");
374          parseRule(map, rule, lexer, newFmt);
375        }
376        lexer.token("}");
377      } else {
378        done = false;
379        while (!done) {
380          parseRuleReference(rule, lexer);
381          done = !lexer.hasToken(",");
382          if (!done)
383            lexer.next();
384        }
385      }
386    }
387    if (!rule.hasChild("documentation") && lexer.hasComments()) {
388      rule.makeElement("documentation").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
389    }
390
391    if (isSimpleSyntax(rule)) {
392      rule.forceElement("source").makeElement("variable").setValue(StructureMapUtilities.AUTO_VAR_NAME);
393      rule.forceElement("target").makeElement("variable").setValue(StructureMapUtilities.AUTO_VAR_NAME);
394      rule.forceElement("target").makeElement("transform").setValue(StructureMapTransform.CREATE.toCode());
395      Element dep = rule.forceElement("dependent").markLocation(rule);
396      dep.makeElement("name").markLocation(rule).setValue(StructureMapUtilities.DEF_GROUP_NAME);
397      dep.addElement("parameter").markLocation(dep).makeElement("valueId").markLocation(dep).setValue(StructureMapUtilities.AUTO_VAR_NAME);
398      dep.addElement("parameter").markLocation(dep).makeElement("valueId").markLocation(dep).setValue(StructureMapUtilities.AUTO_VAR_NAME);
399      // no dependencies - imply what is to be done based on types
400    }
401    if (newFmt) {
402      if (lexer.isConstant()) {
403        if (lexer.isStringConstant()) {
404          rule.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(fixName(lexer.readConstant("ruleName")));
405        } else {
406          rule.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
407        }
408      } else {
409        if (rule.getChildrenByName("source").size() != 1 || !rule.getChildrenByName("source").get(0).hasChild("element"))
410          throw lexer.error("Complex rules must have an explicit name");
411        if (rule.getChildrenByName("source").get(0).hasChild("type"))
412          rule.makeElement("name").setValue(rule.getChildrenByName("source").get(0).getNamedChildValue("element") + Utilities.capitalize(rule.getChildrenByName("source").get(0).getNamedChildValue("type")));
413        else
414          rule.makeElement("name").setValue(rule.getChildrenByName("source").get(0).getNamedChildValue("element"));
415      }
416      lexer.token(";");
417    }
418  }
419
420  private String fixName(String c) {
421    return c.replace("-", "");
422  }
423
424  private void parseRuleReference(Element rule, FHIRLexer lexer) throws FHIRLexerException {
425    Element ref = rule.addElement("dependent").markLocation(lexer.getCurrentLocation());
426    ref.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
427    lexer.token("(");
428    boolean done = false;
429    while (!done) {
430      parseParameter(ref, lexer, false);
431      done = !lexer.hasToken(",");
432      if (!done)
433        lexer.next();
434    }
435    lexer.token(")");
436  }
437
438  private void parseSource(Element rule, FHIRLexer lexer) throws FHIRException {
439    Element source = rule.addElement("source").markLocation(lexer.getCurrentLocation());
440    source.makeElement("context").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
441    if (source.getChildValue("context").equals("search") && lexer.hasToken("(")) {
442      source.makeElement("context").markLocation(lexer.getCurrentLocation()).setValue("@search");
443      lexer.take();
444      SourceLocation loc = lexer.getCurrentLocation();
445      ExpressionNode node = fpe.parse(lexer);
446      source.setUserData(StructureMapUtilities.MAP_SEARCH_EXPRESSION, node);
447      source.makeElement("element").markLocation(loc).setValue(node.toString());
448      lexer.token(")");
449    } else if (lexer.hasToken(".")) {
450      lexer.token(".");
451      source.makeElement("element").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
452    }
453    if (lexer.hasToken(":")) {
454      // type and cardinality
455      lexer.token(":");
456      source.makeElement("type").markLocation(lexer.getCurrentLocation()).setValue(lexer.takeDottedToken());
457    }
458    if (Utilities.isInteger(lexer.getCurrent())) {
459      source.makeElement("min").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
460      lexer.token("..");
461      source.makeElement("max").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
462    }
463    if (lexer.hasToken("default")) {
464      lexer.token("default");
465     source.makeElement("defaultValue").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("default value"));
466    }
467    if (Utilities.existsInList(lexer.getCurrent(), "first", "last", "not_first", "not_last", "only_one")) {
468      source.makeElement("listMode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
469    }
470
471    if (lexer.hasToken("as")) {
472      lexer.take();
473      source.makeElement("variable").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
474    }
475    if (lexer.hasToken("where")) {
476      lexer.take();
477      SourceLocation loc = lexer.getCurrentLocation();
478      ExpressionNode node = fpe.parse(lexer);
479      source.setUserData(StructureMapUtilities.MAP_WHERE_EXPRESSION, node);
480      source.makeElement("condition").markLocation(loc).setValue(node.toString());
481    }
482    if (lexer.hasToken("check")) {
483      lexer.take();
484      SourceLocation loc = lexer.getCurrentLocation();
485      ExpressionNode node = fpe.parse(lexer);
486      source.setUserData(StructureMapUtilities.MAP_WHERE_CHECK, node);
487      source.makeElement("check").markLocation(loc).setValue(node.toString());
488    }
489    if (lexer.hasToken("log")) {
490      lexer.take();
491      SourceLocation loc = lexer.getCurrentLocation();
492      ExpressionNode node = fpe.parse(lexer);
493      source.setUserData(StructureMapUtilities.MAP_WHERE_CHECK, node);
494      source.makeElement("logMessage").markLocation(loc).setValue(lexer.take());
495    }
496  }
497  
498  private void parseTarget(Element rule, FHIRLexer lexer) throws FHIRException {
499    Element target = rule.addElement("target").markLocation(lexer.getCurrentLocation());
500    SourceLocation loc = lexer.getCurrentLocation();
501    String start = lexer.take();
502    if (lexer.hasToken(".")) {
503      target.makeElement("context").markLocation(loc).setValue(start);
504      start = null;
505      lexer.token(".");
506      target.makeElement("element").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
507    }
508    String name;
509    boolean isConstant = false;
510    if (lexer.hasToken("=")) {
511      if (start != null) {
512        target.makeElement("context").markLocation(loc).setValue(start);
513      }
514      lexer.token("=");
515      isConstant = lexer.isConstant();
516      loc = lexer.getCurrentLocation();
517      name = lexer.take();
518    } else {
519      loc = lexer.getCurrentLocation();
520      name = start;
521    }
522
523    if ("(".equals(name)) {
524      // inline fluentpath expression
525      target.makeElement("transform").markLocation(lexer.getCurrentLocation()).setValue(StructureMapTransform.EVALUATE.toCode());
526      loc = lexer.getCurrentLocation();
527      ExpressionNode node = fpe.parse(lexer);
528      target.setUserData(StructureMapUtilities.MAP_EXPRESSION, node);
529      target.addElement("parameter").markLocation(loc).makeElement("valueString").setValue(node.toString());
530      lexer.token(")");
531    } else if (lexer.hasToken("(")) {
532      target.makeElement("transform").markLocation(loc).setValue(name);
533      lexer.token("(");
534      if (target.getChildValue("transform").equals(StructureMapTransform.EVALUATE.toCode())) {
535        parseParameter(target, lexer, true);
536        lexer.token(",");
537        loc = lexer.getCurrentLocation();
538        ExpressionNode node = fpe.parse(lexer);
539        target.setUserData(StructureMapUtilities.MAP_EXPRESSION, node);
540        target.addElement("parameter").markLocation(loc).makeElement("valueString").setValue(node.toString());
541      } else {
542        while (!lexer.hasToken(")")) {
543          parseParameter(target, lexer, true);
544          if (!lexer.hasToken(")"))
545            lexer.token(",");
546        }
547      }
548      lexer.token(")");
549    } else if (name != null) {
550      target.makeElement("transform").markLocation(loc).setValue(StructureMapTransform.COPY.toCode());
551      if (!isConstant) {
552        loc = lexer.getCurrentLocation();
553        String id = name;
554        while (lexer.hasToken(".")) {
555          id = id + lexer.take() + lexer.take();
556        }
557        target.addElement("parameter").markLocation(loc).makeElement("valueId").setValue(id);
558      } else {
559        target.addElement("parameter").markLocation(lexer.getCurrentLocation()).makeElement("valueString").setValue(readConstant(name, lexer));
560      }
561    }
562    if (lexer.hasToken("as")) {
563      lexer.take();
564      target.makeElement("variable").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
565    }
566    while (Utilities.existsInList(lexer.getCurrent(), "first", "last", "share", "collate")) {
567      if (lexer.getCurrent().equals("share")) {
568        target.makeElement("listMode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
569        target.makeElement("listRuleId").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
570      } else {
571        target.makeElement("listMode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
572      }
573    }
574  }
575
576  private void parseParameter(Element ref, FHIRLexer lexer, boolean isTarget) throws FHIRLexerException, FHIRFormatError {
577    boolean r5 = VersionUtilities.isR5Plus(context.getVersion());
578    String name = r5 || isTarget ? "parameter" : "variable";
579    if (ref.hasChildren(name) && !ref.getChildByName(name).isList()) {
580      throw lexer.error("variable on target is not a list, so can't add an element");
581    } else if (!lexer.isConstant()) {
582      ref.addElement(name).markLocation(lexer.getCurrentLocation()).makeElement(r5 ? "valueId" : "value").setValue(lexer.take());
583    } else if (lexer.isStringConstant())
584      ref.addElement(name).markLocation(lexer.getCurrentLocation()).makeElement(r5 ? "valueString" : "value").setValue(lexer.readConstant("??"));
585    else {
586      ref.addElement(name).markLocation(lexer.getCurrentLocation()).makeElement(r5 ? "valueString" : "value").setValue(readConstant(lexer.take(), lexer));
587    }
588  }
589 
590  private void parseInput(Element group, FHIRLexer lexer, boolean newFmt) throws FHIRException {
591    Element input = group.addElement("input").markLocation(lexer.getCurrentLocation());
592    if (newFmt) {
593      input.makeElement("mode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
594    } else
595      lexer.token("input");
596    input.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
597    if (lexer.hasToken(":")) {
598      lexer.token(":");
599      input.makeElement("type").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
600    }
601    if (!newFmt) {
602      lexer.token("as");
603      input.makeElement("mode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
604      if (lexer.hasComments()) {
605        input.makeElement("documentation").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
606      }
607      lexer.skipToken(";");
608    }
609  }
610  
611  private boolean isSimpleSyntax(Element rule) {
612    return
613      (rule.getChildren("source").size() == 1 && rule.getChildren("source").get(0).hasChild("context") && rule.getChildren("source").get(0).hasChild("element") && !rule.getChildren("source").get(0).hasChild("variable")) &&
614        (rule.getChildren("target").size() == 1 && rule.getChildren("target").get(0).hasChild("context") && rule.getChildren("target").get(0).hasChild("element") && !rule.getChildren("target").get(0).hasChild("variable") && 
615           !rule.getChildren("target").get(0).hasChild("parameter")) &&
616        (rule.getChildren("dependent").size() == 0 && rule.getChildren("rule").size() == 0);
617  }
618
619  private String readConstant(String s, FHIRLexer lexer) throws FHIRLexerException {
620    if (Utilities.isInteger(s))
621      return s;
622    else if (Utilities.isDecimal(s, false))
623      return s;
624    else if (Utilities.existsInList(s, "true", "false"))
625      return s;
626    else
627      return lexer.processConstant(s);
628  }
629
630  
631}