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}