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