diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java index e2c44af4bd..f5273ce551 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java @@ -82,7 +82,29 @@ public Element parse(List errors, String text) throws FHIRExc String fid = lexer.takeDottedToken(); Element e = result.makeElement(fid).markLocation(lexer.getCurrentLocation()); lexer.token("="); - e.setValue(lexer.readConstant("meta value")); + String multiline = lexer.getCurrent(); + if (fid.equals("description")) { + String descr = lexer.readConstant("description"); + if ("\"\"".equals(multiline)) { + descr = lexer.readConstant("description multiline"); + if (descr.startsWith("\r")) { + descr = descr.substring(1); + } + if (descr.startsWith("\n")) { + descr = descr.substring(1); + } + if (descr.endsWith("\n")) { + descr = descr.substring(0, descr.length()-1); + } + if (descr.endsWith("\r")) { + descr = descr.substring(0, descr.length()-1); + } + lexer.skipToken("\"\""); + } + e.setValue(descr); + } else { + e.setValue(lexer.readConstant("meta value")); + } } lexer.setMetadataFormat(false); if (!result.hasChild("status")) { @@ -434,6 +456,16 @@ private void parseRuleReference(Element rule, FHIRLexer lexer) throws FHIRLexerE } lexer.token(")"); } + + private String removeQuotedOrBacktick(String token) { + if (token.startsWith("`") && token.endsWith("`")) { + return token.substring(1,token.length()-1); + } + if (token.startsWith("\"") && token.endsWith("\"")) { + return token.substring(1,token.length()-1); + } + return token; + } private void parseSource(Element rule, FHIRLexer lexer) throws FHIRException { Element source = rule.addElement("source").markLocation(lexer.getCurrentLocation()); @@ -448,7 +480,7 @@ private void parseSource(Element rule, FHIRLexer lexer) throws FHIRException { lexer.token(")"); } else if (lexer.hasToken(".")) { lexer.token("."); - source.makeElement("element").markLocation(lexer.getCurrentLocation()).setValue(lexer.take()); + source.makeElement("element").markLocation(lexer.getCurrentLocation()).setValue(removeQuotedOrBacktick(lexer.take())); } if (lexer.hasToken(":")) { // type and cardinality @@ -490,8 +522,8 @@ private void parseSource(Element rule, FHIRLexer lexer) throws FHIRException { lexer.take(); SourceLocation loc = lexer.getCurrentLocation(); ExpressionNode node = fpe.parse(lexer); - source.setUserData(StructureMapUtilities.MAP_WHERE_CHECK, node); - source.makeElement("logMessage").markLocation(loc).setValue(lexer.take()); + source.setUserData(StructureMapUtilities.MAP_WHERE_LOG, node); + source.makeElement("logMessage").markLocation(loc).setValue(node.toString()); } } @@ -503,7 +535,7 @@ private void parseTarget(Element rule, FHIRLexer lexer) throws FHIRException { target.makeElement("context").markLocation(loc).setValue(start); start = null; lexer.token("."); - target.makeElement("element").markLocation(lexer.getCurrentLocation()).setValue(lexer.take()); + target.makeElement("element").markLocation(lexer.getCurrentLocation()).setValue(removeQuotedOrBacktick(lexer.take())); } String name; boolean isConstant = false; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java index 0cabfd16b0..672ef78f94 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/StructureMapUtilities.java @@ -41,18 +41,18 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Element; +import org.hl7.fhir.r5.elementmodel.FmlParser; import org.hl7.fhir.r5.elementmodel.Manager; import org.hl7.fhir.r5.elementmodel.Property; +import org.hl7.fhir.r5.elementmodel.ParserBase.ValidationPolicy; import org.hl7.fhir.r5.fhirpath.ExpressionNode; -import org.hl7.fhir.r5.fhirpath.FHIRLexer; import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; import org.hl7.fhir.r5.fhirpath.TypeDetails; import org.hl7.fhir.r5.fhirpath.ExpressionNode.CollectionStatus; -import org.hl7.fhir.r5.fhirpath.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r5.fhirpath.TypeDetails.ProfiledType; +import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.model.*; import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; -import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupUnmappedMode; import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent; import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; @@ -66,17 +66,21 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.StructureMap.*; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.r5.renderers.TerminologyRenderer; import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlNode; +import net.sourceforge.plantuml.utils.Log; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.*; @@ -149,8 +153,9 @@ public static String render(StructureMap map) { b.append("/// status = \""+map.getStatus().toCode()+"\"\r\n"); b.append("\r\n"); if (map.getDescription() != null) { - renderMultilineDoco(b, map.getDescription(), 0); - b.append("\r\n"); + b.append("/// description = \"\"\"\r\n"); + renderMultilineDoco(b, map.getDescription(), 0, false); + b.append("\"\"\"\r\n"); } renderConceptMaps(b, map); renderUses(b, map); @@ -290,7 +295,7 @@ public static String groupToString(StructureMapGroupComponent g) { private static void renderGroup(StringBuilder b, StructureMapGroupComponent g) { if (g.hasDocumentation()) { - renderMultilineDoco(b, g.getDocumentation(), 0); + renderMultilineDoco(b, g.getDocumentation(), 0, true); } b.append("group "); b.append(g.getName()); @@ -340,8 +345,8 @@ public static String ruleToString(StructureMapGroupRuleComponent r) { } private static void renderRule(StringBuilder b, StructureMapGroupRuleComponent r, int indent) { - if (r.hasFormatCommentPre()) { - renderMultilineDoco(b, r.getFormatCommentsPre(), indent); + if (r.hasDocumentation()) { + renderMultilineDoco(b, r.getDocumentation(), indent, true); } for (int i = 0; i < indent; i++) b.append(' '); @@ -589,37 +594,33 @@ else if (rtp.hasValueIntegerType()) } private static void renderDoco(StringBuilder b, String doco) { + renderDoco(b, doco, true); + } + + private static void renderDoco(StringBuilder b, String doco, boolean addComment) { if (Utilities.noString(doco)) return; if (b != null && b.length() > 1 && b.charAt(b.length() - 1) != '\n' && b.charAt(b.length() - 1) != ' ') { b.append(" "); } - b.append("// "); + if (addComment) { + b.append("// "); + } b.append(doco.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")); } - private static void renderMultilineDoco(StringBuilder b, String doco, int indent) { + private static void renderMultilineDoco(StringBuilder b, String doco, int indent, boolean addComment) { if (Utilities.noString(doco)) return; String[] lines = doco.split("\\r?\\n"); for (String line : lines) { for (int i = 0; i < indent; i++) b.append(' '); - renderDoco(b, line); + renderDoco(b, line, addComment); b.append("\r\n"); } } - private static void renderMultilineDoco(StringBuilder b, List doco, int indent) { - if (doco == null || doco.isEmpty()) - return; - for (String line : doco) { - for (int i = 0; i < indent; i++) - b.append(' '); - renderDoco(b, line); - b.append("\r\n"); - } - } - + public ITransformerServices getServices() { return services; } @@ -629,588 +630,29 @@ public IWorkerContext getWorker() { } public StructureMap parse(String text, String srcName) throws FHIRException { - FHIRLexer lexer = new FHIRLexer(Utilities.stripBOM(text), srcName, true, true); - if (lexer.done()) - throw lexer.error("Map Input cannot be empty"); - StructureMap result = new StructureMap(); - if (lexer.hasToken("map")) { - lexer.token("map"); - result.setUrl(lexer.readConstant("url")); - lexer.token("="); - result.setName(lexer.readConstant("name")); - result.setDescription(lexer.getAllComments()); - result.setStatus(PublicationStatus.DRAFT); - } - while (lexer.hasToken("///")) { - lexer.next(); - String fid = lexer.takeDottedToken(); - lexer.token("="); - switch (fid) { - case "url" : - result.setUrl(lexer.readConstant("url")); - break; - case "name" : - result.setName(lexer.readConstant("name")); - break; - case "title" : - result.setTitle(lexer.readConstant("title")); - break; - case "description" : - result.setTitle(lexer.readConstant("description")); - break; - case "status" : - result.setStatus(PublicationStatus.fromCode(lexer.readConstant("status"))); - break; - default: - lexer.readConstant("nothing"); - // nothing - } - } - if (!result.hasId() && result.hasName()) { - String id = Utilities.makeId(result.getName()); - if (!Utilities.noString(id)) { - result.setId(id); - } - } - if (!result.hasStatus()) { - result.setStatus(PublicationStatus.DRAFT); - } - if (!result.hasDescription() && result.hasTitle()) { - result.setDescription(result.getTitle()); - } - - while (lexer.hasToken("conceptmap")) - parseConceptMap(result, lexer); - - while (lexer.hasToken("uses")) - parseUses(result, lexer); - while (lexer.hasToken("imports")) - parseImports(result, lexer); - - while (lexer.hasToken("conceptmap")) - parseConceptMap(result, lexer); - - while (!lexer.done()) { - parseGroup(result, lexer); - } - - return result; - } - - - private void parseConceptMap(StructureMap result, FHIRLexer lexer) throws FHIRLexerException { - ConceptMap map = new ConceptMap(); - map.addFormatCommentsPre(lexer.getComments()); - lexer.token("conceptmap"); - String id = lexer.readConstant("map id"); - if (id.startsWith("#")) - throw lexer.error("Concept Map identifier must start with #"); - map.setId(id); - map.setStatus(PublicationStatus.DRAFT); // todo: how to add this to the text format - result.getContained().add(map); - lexer.token("{"); - // lexer.token("source"); - // map.setSource(new UriType(lexer.readConstant("source"))); - // lexer.token("target"); - // map.setSource(new UriType(lexer.readConstant("target"))); - Map prefixes = new HashMap(); - while (lexer.hasToken("prefix")) { - lexer.token("prefix"); - String n = lexer.take(); - lexer.token("="); - String v = lexer.readConstant("prefix url"); - prefixes.put(n, v); - } - while (lexer.hasToken("unmapped")) { - List comments = lexer.cloneComments(); - lexer.token("unmapped"); - lexer.token("for"); - String n = readPrefix(prefixes, lexer); - ConceptMapGroupComponent g = getGroup(map, n, null); - g.addFormatCommentsPre(comments); - lexer.token("="); - String v = lexer.take(); - if (v.equals("provided")) { - g.getUnmapped().setMode(ConceptMapGroupUnmappedMode.USESOURCECODE); - } else - throw lexer.error("Only unmapped mode PROVIDED is supported at this time"); - } - while (!lexer.hasToken("}")) { - List comments = lexer.cloneComments(); - String srcs = readPrefix(prefixes, lexer); - lexer.token(":"); - String sc = lexer.getCurrent().startsWith("\"") ? lexer.readConstant("code") : lexer.take(); - ConceptMapRelationship rel = readRelationship(lexer); - String tgts = readPrefix(prefixes, lexer); - ConceptMapGroupComponent g = getGroup(map, srcs, tgts); - SourceElementComponent e = g.addElement(); - e.addFormatCommentsPre(comments); - e.setCode(sc); - if (e.getCode().startsWith("\"")) { - e.setCode(lexer.processConstant(e.getCode())); - } - TargetElementComponent tgt = e.addTarget(); - tgt.setRelationship(rel); - lexer.token(":"); - tgt.setCode(lexer.take()); - if (tgt.getCode().startsWith("\"")) { - tgt.setCode(lexer.processConstant(tgt.getCode())); - } - // tgt.setComment(lexer.getAllComments()); - } - map.addFormatCommentsPost(lexer.getComments()); - lexer.token("}"); - } - - - - private ConceptMapGroupComponent getGroup(ConceptMap map, String srcs, String tgts) { - for (ConceptMapGroupComponent grp : map.getGroup()) { - if (grp.getSource().equals(srcs)) - if (!grp.hasTarget() || tgts == null || tgts.equals(grp.getTarget())) { - if (!grp.hasTarget() && tgts != null) - grp.setTarget(tgts); - return grp; - } - } - ConceptMapGroupComponent grp = map.addGroup(); - grp.setSource(srcs); - grp.setTarget(tgts); - return grp; - } - - - - private String readPrefix(Map prefixes, FHIRLexer lexer) throws FHIRLexerException { - String prefix = lexer.take(); - if (!prefixes.containsKey(prefix)) - throw lexer.error("Unknown prefix '" + prefix + "'"); - return prefixes.get(prefix); - } - - - private ConceptMapRelationship readRelationship(FHIRLexer lexer) throws FHIRLexerException { - String token = lexer.take(); - if (token.equals("-")) - return ConceptMapRelationship.RELATEDTO; - if (token.equals("==")) - return ConceptMapRelationship.EQUIVALENT; - if (token.equals("!=")) - return ConceptMapRelationship.NOTRELATEDTO; - if (token.equals("<=")) - return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; - if (token.equals(">=")) - return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; - throw lexer.error("Unknown relationship token '" + token + "'"); - } - - - private void parseUses(StructureMap result, FHIRLexer lexer) throws FHIRException { - lexer.token("uses"); - StructureMapStructureComponent st = result.addStructure(); - st.setUrl(lexer.readConstant("url")); - if (lexer.hasToken("alias")) { - lexer.token("alias"); - st.setAlias(lexer.take()); - } - lexer.token("as"); - String doco; - if (lexer.getCurrent().equals("source")) { - st.setMode(StructureMapModelMode.SOURCE); - doco = lexer.tokenWithTrailingComment("source"); - } else if (lexer.getCurrent().equals("target")) { - st.setMode(StructureMapModelMode.TARGET); - doco = lexer.tokenWithTrailingComment("target"); - } else { - throw lexer.error("Found '"+lexer.getCurrent()+"' expecting 'source' or 'target'"); - } - if (lexer.hasToken(";")) { - doco = lexer.tokenWithTrailingComment(";"); - } - st.setDocumentation(doco); - } - - - - private void parseImports(StructureMap result, FHIRLexer lexer) throws FHIRException { - lexer.token("imports"); - result.addImport(lexer.readConstant("url")); - lexer.skipToken(";"); - } - - private void parseGroup(StructureMap result, FHIRLexer lexer) throws FHIRException { - String comment = lexer.getAllComments(); - lexer.token("group"); - StructureMapGroupComponent group = result.addGroup(); - if (comment != null) { - group.setDocumentation(comment); - } - boolean newFmt = false; - if (lexer.hasToken("for")) { - lexer.token("for"); - if ("type".equals(lexer.getCurrent())) { - lexer.token("type"); - lexer.token("+"); - lexer.token("types"); - group.setTypeMode(StructureMapGroupTypeMode.TYPEANDTYPES); - } else { - lexer.token("types"); - group.setTypeMode(StructureMapGroupTypeMode.TYPES); - } - } - group.setName(lexer.take()); - if (lexer.hasToken("(")) { - newFmt = true; - lexer.take(); - while (!lexer.hasToken(")")) { - parseInput(group, lexer, true); - if (lexer.hasToken(",")) - lexer.token(","); - } - lexer.take(); - } - if (lexer.hasToken("extends")) { - lexer.next(); - group.setExtends(lexer.take()); - } - if (newFmt) { - if (lexer.hasToken("<")) { - lexer.token("<"); - lexer.token("<"); - if (lexer.hasToken("types")) { - group.setTypeMode(StructureMapGroupTypeMode.TYPES); - lexer.token("types"); - } else { - lexer.token("type"); - lexer.token("+"); - group.setTypeMode(StructureMapGroupTypeMode.TYPEANDTYPES); - } - lexer.token(">"); - lexer.token(">"); - } - lexer.token("{"); - } - if (newFmt) { - while (!lexer.hasToken("}")) { - if (lexer.done()) - throw lexer.error("premature termination expecting 'endgroup'"); - parseRule(result, group.getRule(), lexer, true); - } - } else { - while (lexer.hasToken("input")) - parseInput(group, lexer, false); - while (!lexer.hasToken("endgroup")) { - if (lexer.done()) - throw lexer.error("premature termination expecting 'endgroup'"); - parseRule(result, group.getRule(), lexer, false); - } - } - group.addFormatCommentsPost(lexer.getComments()); - lexer.next(); - if (newFmt && lexer.hasToken(";")) - lexer.next(); - } - - - - private void parseInput(StructureMapGroupComponent group, FHIRLexer lexer, boolean newFmt) throws FHIRException { - StructureMapGroupInputComponent input = group.addInput(); - if (newFmt) { - input.setMode(StructureMapInputMode.fromCode(lexer.take())); - } else - lexer.token("input"); - input.setName(lexer.take()); - if (lexer.hasToken(":")) { - lexer.token(":"); - input.setType(lexer.take()); - } - if (!newFmt) { - lexer.token("as"); - input.setMode(StructureMapInputMode.fromCode(lexer.take())); - input.setDocumentation(lexer.getAllComments()); - lexer.skipToken(";"); - } - } - - - - private void parseRule(StructureMap map, List list, FHIRLexer lexer, boolean newFmt) throws FHIRException { - StructureMapGroupRuleComponent rule = new StructureMapGroupRuleComponent(); - if (!newFmt) { - rule.setName(lexer.takeDottedToken()); - lexer.token(":"); - lexer.token("for"); - } else { - rule.addFormatCommentsPre(lexer.getComments()); - } - list.add(rule); - boolean done = false; - while (!done) { - parseSource(rule, lexer); - done = !lexer.hasToken(","); - if (!done) - lexer.next(); - } - if ((newFmt && lexer.hasToken("->")) || (!newFmt && lexer.hasToken("make"))) { - lexer.token(newFmt ? "->" : "make"); - done = false; - while (!done) { - parseTarget(rule, lexer); - done = !lexer.hasToken(","); - if (!done) - lexer.next(); - } - } - if (lexer.hasToken("then")) { - lexer.token("then"); - if (lexer.hasToken("{")) { - lexer.token("{"); - while (!lexer.hasToken("}")) { - if (lexer.done()) - throw lexer.error("premature termination expecting '}' in nested group"); - parseRule(map, rule.getRule(), lexer, newFmt); - } - lexer.token("}"); - } else { - done = false; - while (!done) { - parseRuleReference(rule, lexer); - done = !lexer.hasToken(","); - if (!done) - lexer.next(); - } - } - } - if (isSimpleSyntax(rule)) { - rule.getSourceFirstRep().setVariable(AUTO_VAR_NAME); - rule.getTargetFirstRep().setVariable(AUTO_VAR_NAME); - rule.getTargetFirstRep().setTransform(StructureMapTransform.CREATE); // with no parameter - e.g. imply what is to be created - // no dependencies - imply what is to be done based on types - } - if (newFmt) { - if (lexer.isConstant()) { - if (lexer.isStringConstant()) { - rule.setName(fixName(lexer.readConstant("ruleName"))); - } else { - rule.setName(lexer.take()); - } - } else { - if (rule.getSource().size() != 1 || !rule.getSourceFirstRep().hasElement() && exceptionsForChecks ) - throw lexer.error("Complex rules must have an explicit name"); - if (rule.getSourceFirstRep().hasType()) - rule.setName(rule.getSourceFirstRep().getElement() + Utilities.capitalize(rule.getSourceFirstRep().getType())); - else - rule.setName(rule.getSourceFirstRep().getElement()); - } - String doco = lexer.tokenWithTrailingComment(";"); - if (doco != null) { - rule.setDocumentation(doco); - } - } - } - - private String fixName(String c) { - return c.replace("-", ""); - } - - private boolean isSimpleSyntax(StructureMapGroupRuleComponent rule) { - return - (rule.getSource().size() == 1 && rule.getSourceFirstRep().hasContext() && rule.getSourceFirstRep().hasElement() && !rule.getSourceFirstRep().hasVariable()) && - (rule.getTarget().size() == 1 && rule.getTargetFirstRep().hasContext() && rule.getTargetFirstRep().hasElement() && !rule.getTargetFirstRep().hasVariable() && !rule.getTargetFirstRep().hasParameter()) && - (rule.getDependent().size() == 0 && rule.getRule().size() == 0); - } - - - - private void parseRuleReference(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRLexerException { - StructureMapGroupRuleDependentComponent ref = rule.addDependent(); - ref.setName(lexer.take()); - lexer.token("("); - boolean done = false; - while (!done) { - parseParameter(ref, lexer); - done = !lexer.hasToken(","); - if (!done) - lexer.next(); - } - lexer.token(")"); - } - - - private void parseSource(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException { - StructureMapGroupRuleSourceComponent source = rule.addSource(); - source.setContext(lexer.take()); - if (source.getContext().equals("search") && lexer.hasToken("(")) { - source.setContext("@search"); - lexer.take(); - ExpressionNode node = fpe.parse(lexer); - source.setUserData(MAP_SEARCH_EXPRESSION, node); - source.setElement(node.toString()); - lexer.token(")"); - } else if (lexer.hasToken(".")) { - lexer.token("."); - source.setElement(readAsStringOrProcessedConstant(lexer.take(), lexer)); - } - if (lexer.hasToken(":")) { - // type and cardinality - lexer.token(":"); - source.setType(lexer.takeDottedToken()); - } - if (Utilities.isInteger(lexer.getCurrent())) { - source.setMin(lexer.takeInt()); - lexer.token(".."); - source.setMax(lexer.take()); - } - if (lexer.hasToken("default")) { - lexer.token("default"); - source.setDefaultValue(lexer.readConstant("default value")); - } - if (Utilities.existsInList(lexer.getCurrent(), "first", "last", "not_first", "not_last", "only_one")) - source.setListMode(StructureMapSourceListMode.fromCode(lexer.take())); - - if (lexer.hasToken("as")) { - lexer.take(); - source.setVariable(lexer.take()); - } - if (lexer.hasToken("where")) { - lexer.take(); - ExpressionNode node = fpe.parse(lexer); - source.setUserData(MAP_WHERE_EXPRESSION, node); - source.setCondition(node.toString()); - } - if (lexer.hasToken("check")) { - lexer.take(); - ExpressionNode node = fpe.parse(lexer); - source.setUserData(MAP_WHERE_CHECK, node); - source.setCheck(node.toString()); - } - if (lexer.hasToken("log")) { - lexer.take(); - ExpressionNode node = fpe.parse(lexer); - source.setUserData(MAP_WHERE_LOG, node); - source.setLogMessage(node.toString()); - } - } - - private String readAsStringOrProcessedConstant(String s, FHIRLexer lexer) throws FHIRLexerException { - if (s.startsWith("\"") || s.startsWith("`")) - return lexer.processConstant(s); - else - return s; - } - - private void parseTarget(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException { - StructureMapGroupRuleTargetComponent target = rule.addTarget(); - String start = lexer.take(); - if (lexer.hasToken(".")) { - target.setContext(start); - start = null; - lexer.token("."); - target.setElement(lexer.take()); - } - String name; - boolean isConstant = false; - if (lexer.hasToken("=")) { - if (start != null) - target.setContext(start); - lexer.token("="); - isConstant = lexer.isConstant(); - name = lexer.take(); - } else - name = start; - - if ("(".equals(name)) { - // inline fluentpath expression - target.setTransform(StructureMapTransform.EVALUATE); - ExpressionNode node = fpe.parse(lexer); - target.setUserData(MAP_EXPRESSION, node); - target.addParameter().setValue(new StringType(node.toString())); - lexer.token(")"); - } else if (lexer.hasToken("(")) { - target.setTransform(StructureMapTransform.fromCode(name)); - lexer.token("("); - if (target.getTransform() == StructureMapTransform.EVALUATE) { - parseParameter(target, lexer); - lexer.token(","); - ExpressionNode node = fpe.parse(lexer); - target.setUserData(MAP_EXPRESSION, node); - target.addParameter().setValue(new StringType(node.toString())); - } else { - while (!lexer.hasToken(")")) { - parseParameter(target, lexer); - if (!lexer.hasToken(")")) - lexer.token(","); - } - } - lexer.token(")"); - } else if (name != null) { - if (target.getContext() != null) { - target.setTransform(StructureMapTransform.COPY); - if (!isConstant) { - String id = name; - while (lexer.hasToken(".")) { - id = id + lexer.take() + lexer.take(); - } - target.addParameter().setValue(new IdType(id)); - } else - target.addParameter().setValue(readConstant(name, lexer)); - } else { - target.setContext(name); - } - } - if (lexer.hasToken("as")) { - lexer.take(); - target.setVariable(lexer.take()); - } - while (Utilities.existsInList(lexer.getCurrent(), "first", "last", "share", "collate")) { - if (lexer.getCurrent().equals("share")) { - target.addListMode(StructureMapTargetListMode.SHARE); - lexer.next(); - target.setListRuleId(lexer.take()); - } else { - if (lexer.getCurrent().equals("first")) - target.addListMode(StructureMapTargetListMode.FIRST); - else - target.addListMode(StructureMapTargetListMode.LAST); - lexer.next(); - } - } - } - - - private void parseParameter(StructureMapGroupRuleDependentComponent ref, FHIRLexer lexer) throws FHIRLexerException, FHIRFormatError { - if (!lexer.isConstant()) { - ref.addParameter().setValue(new IdType(lexer.take())); - } else if (lexer.isStringConstant()) - ref.addParameter().setValue(new StringType(lexer.readConstant("??"))); - else { - ref.addParameter().setValue(readConstant(lexer.take(), lexer)); - } - } - - private void parseParameter(StructureMapGroupRuleTargetComponent target, FHIRLexer lexer) throws FHIRLexerException, FHIRFormatError { - if (!lexer.isConstant()) { - target.addParameter().setValue(new IdType(lexer.take())); - } else if (lexer.isStringConstant()) - target.addParameter().setValue(new StringType(lexer.readConstant("??"))); - else { - target.addParameter().setValue(readConstant(lexer.take(), lexer)); - } - } - - - private DataType readConstant(String s, FHIRLexer lexer) throws FHIRLexerException { - if (Utilities.isInteger(s)) - return new IntegerType(s); - else if (Utilities.isDecimal(s, false)) - return new DecimalType(s); - else if (Utilities.existsInList(s, "true", "false")) - return new BooleanType(s.equals("true")); - else - return new StringType(lexer.processConstant(s)); + IWorkerContext context = this.getWorker(); + if (!(context.getVersion().equals("5.0.0"))) { + throw new FHIRException("FHIR version needs to be 5.0.0"); + } + FmlParser fp = new FmlParser(context); + fp.setupValidation(ValidationPolicy.EVERYTHING); + List errors = new ArrayList(); + Element res = fp.parse(errors, Utilities.stripBOM(text)); + if (res == null) { + Log.error(errors.toString()); + throw new FHIRException("Unable to parse Map Source for "+srcName + " Details "+errors.toString()); + } + ByteArrayOutputStream boas = new ByteArrayOutputStream(); + try { + new org.hl7.fhir.r5.elementmodel.JsonParser(this.getWorker()).compose(res, boas, + IParser.OutputStyle.PRETTY, + null); + return (StructureMap) new org.hl7.fhir.r5.formats.JsonParser().parse( new ByteArrayInputStream(boas.toByteArray())); + } catch (IOException e) { + throw new FHIRException(e.getMessage(), e); + } } - public StructureDefinition getTargetType(StructureMap map) throws FHIRException { boolean found = false; StructureDefinition res = null; @@ -1329,11 +771,11 @@ private void executeRule(String indent, TransformContext context, StructureMap m for (StructureMapGroupRuleComponent childrule : rule.getRule()) { executeRule(indent + " ", context, map, v, group, childrule, false); } - } else if (rule.hasDependent()) { + } else if (rule.hasDependent() && !checkisSimple(rule)) { for (StructureMapGroupRuleDependentComponent dependent : rule.getDependent()) { executeDependency(indent + " ", context, map, v, group, dependent); } - } else if (rule.getSource().size() == 1 && rule.getSourceFirstRep().hasVariable() && rule.getTarget().size() == 1 && rule.getTargetFirstRep().hasVariable() && rule.getTargetFirstRep().getTransform() == StructureMapTransform.CREATE && !rule.getTargetFirstRep().hasParameter()) { + } else if (checkisSimple(rule)) { // simple inferred, map by type if (debug) { log(v.summary()); diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/StructureMapUtilitiesTest.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/StructureMapUtilitiesTest.java index d88820f7ed..8f938aeff3 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/StructureMapUtilitiesTest.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/StructureMapUtilitiesTest.java @@ -30,7 +30,7 @@ public class StructureMapUtilitiesTest implements ITransformerServices { @BeforeAll static public void setUp() throws Exception { FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager.Builder().build(); - context = TestingUtilities.getWorkerContext(pcm.loadPackage("hl7.fhir.r4.core", "4.0.1")); + context = TestingUtilities.getWorkerContext(pcm.loadPackage("hl7.fhir.r5.core", "5.0.0")); } @Test @@ -84,6 +84,22 @@ public void testWhereClause() throws IOException, FHIRException { FHIRPathEngine fp = new FHIRPathEngine(context); assertEquals("true", fp.evaluateToString(target, "rest.resource.interaction.where(code='create').exists()")); } + + @Test + public void testShortSyntax() throws IOException, FHIRException { + StructureMapUtilities scu = new StructureMapUtilities(context, null); + scu.setDebug(true); + String fileMap = TestingUtilities.loadTestResource("r5", "structure-mapping", "syntaxshort.map"); + Element source = Manager.parseSingle(context, TestingUtilities.loadTestResourceStream("r4", "examples", "capabilitystatement-example.json"), FhirFormat.JSON); + StructureMap structureMap = scu.parse(fileMap, "syntaxshort"); + + Element target = Manager.build(context, scu.getTargetType(structureMap)); + scu.transform(null, source, structureMap, target); + FHIRPathEngine fp = new FHIRPathEngine(context); + assertEquals("ACME-EHR", fp.evaluateToString(target, "name")); + assertEquals("ACME EHR capability statement", fp.evaluateToString(target, "title")); + } + private void assertSerializeDeserialize(StructureMap structureMap) { Assertions.assertEquals("syntax", structureMap.getName()); @@ -96,8 +112,8 @@ private void assertSerializeDeserialize(StructureMap structureMap) { Assertions.assertEquals("http://hl7.org/fhir/StructureDefinition/Basic", structureMap.getStructure().get(1).getUrl()); Assertions.assertEquals("Target Documentation", structureMap.getStructure().get(1).getDocumentation()); Assertions.assertEquals("Groups\r\nrule for patient group", structureMap.getGroup().get(0).getDocumentation()); - Assertions.assertEquals("Comment to rule", structureMap.getGroup().get(0).getRule().get(0).getFormatCommentsPre().get(0)); - Assertions.assertEquals("Copy identifier short syntax", structureMap.getGroup().get(0).getRule().get(1).getFormatCommentsPre().get(0)); + Assertions.assertEquals("Comment to rule", structureMap.getGroup().get(0).getRule().get(0).getDocumentation()); + Assertions.assertEquals("Copy identifier short syntax", structureMap.getGroup().get(0).getRule().get(1).getDocumentation()); StructureMapGroupRuleTargetComponent target = structureMap.getGroup().get(0).getRule().get(2).getTarget().get(1); Assertions.assertEquals("'urn:uuid:' + r.lower()", target.getParameter().get(0).toString());