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