
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}