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