Add ElementModel based StructureMap parser

This commit is contained in:
Grahame Grieve 2023-01-18 12:32:06 +11:00
parent c93fd840d2
commit c9833f94d3
5 changed files with 558 additions and 5 deletions

View File

@ -59,6 +59,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.ElementDecoration; import org.hl7.fhir.utilities.ElementDecoration;
import org.hl7.fhir.utilities.ElementDecoration.DecorationType; import org.hl7.fhir.utilities.ElementDecoration.DecorationType;
import org.hl7.fhir.utilities.SourceLocation;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -509,8 +510,12 @@ public class Element extends Base {
public Base makeProperty(int hash, String name) throws FHIRException { public Base makeProperty(int hash, String name) throws FHIRException {
if (isPrimitive() && (hash == "value".hashCode())) { if (isPrimitive() && (hash == "value".hashCode())) {
return new StringType(value); return new StringType(value);
} else {
return makeElement(name);
} }
}
public Element makeElement(String name) throws FHIRException {
if (children == null) if (children == null)
children = new ArrayList<Element>(); children = new ArrayList<Element>();
@ -539,6 +544,29 @@ public class Element extends Base {
throw new Error("Unrecognised name "+name+" on "+this.name); throw new Error("Unrecognised name "+name+" on "+this.name);
} }
public Element forceElement(String name) throws FHIRException {
if (children == null)
children = new ArrayList<Element>();
// look through existing children
for (Element child : children) {
if (child.getName().equals(name)) {
return child;
}
}
for (Property p : property.getChildProperties(this.name, type)) {
if (p.getName().equals(name)) {
Element ne = new Element(name, p);
children.add(ne);
return ne;
}
}
throw new Error("Unrecognised name "+name+" on "+this.name);
}
private int maxToInt(String max) { private int maxToInt(String max) {
if (max.equals("*")) if (max.equals("*"))
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
@ -598,6 +626,12 @@ public class Element extends Base {
return this; return this;
} }
public Element markLocation(SourceLocation loc) {
this.line = loc.getLine();
this.col = loc.getColumn();
return this;
}
public void clearDecorations() { public void clearDecorations() {
clearUserData("fhir.decorations"); clearUserData("fhir.decorations");
for (Element e : children) { for (Element e : children) {
@ -1259,4 +1293,26 @@ public class Element extends Base {
} }
} }
public void setElement(String string, Element map) {
throw new Error("Not done yet");
}
public Element addElement(String name) {
if (children == null)
children = new ArrayList<Element>();
for (Property p : property.getChildProperties(this.name, type)) {
if (p.getName().equals(name)) {
if (!p.isList()) {
throw new Error(name+" on "+this.name+" is not a list, so can't add an element");
}
Element ne = new Element(name, p);
children.add(ne);
return ne;
}
}
throw new Error("Unrecognised name "+name+" on "+this.name);
}
} }

View File

@ -122,7 +122,7 @@ public class Manager {
public static Element build(IWorkerContext context, StructureDefinition sd) { public static Element build(IWorkerContext context, StructureDefinition sd) {
Property p = new Property(context, sd.getSnapshot().getElementFirstRep(), sd); Property p = new Property(context, sd.getSnapshot().getElementFirstRep(), sd);
Element e = new Element(null, p); Element e = new Element(p.getName(), p);
return e; return e;
} }

View File

@ -77,6 +77,7 @@ public class FHIRLexer {
private int id; private int id;
private String name; private String name;
private boolean liquidMode; // in liquid mode, || terminates the expression and hands the parser back to the host private boolean liquidMode; // in liquid mode, || terminates the expression and hands the parser back to the host
private SourceLocation commentLocation;
public FHIRLexer(String source, String name) throws FHIRLexerException { public FHIRLexer(String source, String name) throws FHIRLexerException {
this.source = source == null ? "" : source; this.source = source == null ? "" : source;
@ -298,12 +299,14 @@ public class FHIRLexer {
boolean done = false; boolean done = false;
while (cursor < source.length() && !done) { while (cursor < source.length() && !done) {
if (cursor < source.length() -1 && "//".equals(source.substring(cursor, cursor+2))) { if (cursor < source.length() -1 && "//".equals(source.substring(cursor, cursor+2))) {
commentLocation = currentLocation;
int start = cursor+2; int start = cursor+2;
while (cursor < source.length() && !((source.charAt(cursor) == '\r') || source.charAt(cursor) == '\n')) { while (cursor < source.length() && !((source.charAt(cursor) == '\r') || source.charAt(cursor) == '\n')) {
cursor++; cursor++;
} }
comments.add(source.substring(start, cursor).trim()); comments.add(source.substring(start, cursor).trim());
} else if (cursor < source.length() - 1 && "/*".equals(source.substring(cursor, cursor+2))) { } else if (cursor < source.length() - 1 && "/*".equals(source.substring(cursor, cursor+2))) {
commentLocation = currentLocation;
int start = cursor+2; int start = cursor+2;
while (cursor < source.length() - 1 && !"*/".equals(source.substring(cursor, cursor+2))) { while (cursor < source.length() - 1 && !"*/".equals(source.substring(cursor, cursor+2))) {
last13 = currentLocation.checkChar(source.charAt(cursor), last13); last13 = currentLocation.checkChar(source.charAt(cursor), last13);
@ -533,5 +536,8 @@ public class FHIRLexer {
public void setLiquidMode(boolean liquidMode) { public void setLiquidMode(boolean liquidMode) {
this.liquidMode = liquidMode; this.liquidMode = liquidMode;
} }
public SourceLocation getCommentLocation() {
return this.commentLocation;
}
} }

View File

@ -42,6 +42,7 @@ import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.elementmodel.Property; import org.hl7.fhir.r5.elementmodel.Property;
import org.hl7.fhir.r5.model.*; import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
@ -68,6 +69,7 @@ import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.SourceLocation;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.validation.ValidationOptions;
@ -638,6 +640,31 @@ public class StructureMapUtilities {
return result; return result;
} }
public Element parseEM(String text, String srcName) throws FHIRException {
FHIRLexer lexer = new FHIRLexer(text, srcName);
if (lexer.done())
throw lexer.error("Map Input cannot be empty");
lexer.token("map");
Element result = Manager.build(worker, worker.fetchTypeDefinition("StructureMap"));
result.makeElement("url").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("url"));
lexer.token("=");
result.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("name"));
result.makeElement("description").markLocation(lexer.getCurrentLocation()).setValue(lexer.getAllComments());
while (lexer.hasToken("conceptmap"))
parseConceptMapEM(result, lexer);
while (lexer.hasToken("uses"))
parseUsesEM(result, lexer);
while (lexer.hasToken("imports"))
parseImportsEM(result, lexer);
while (!lexer.done()) {
parseGroupEM(result, lexer);
}
return result;
}
private void parseConceptMap(StructureMap result, FHIRLexer lexer) throws FHIRLexerException { private void parseConceptMap(StructureMap result, FHIRLexer lexer) throws FHIRLexerException {
lexer.token("conceptmap"); lexer.token("conceptmap");
ConceptMap map = new ConceptMap(); ConceptMap map = new ConceptMap();
@ -694,6 +721,64 @@ public class StructureMapUtilities {
lexer.token("}"); lexer.token("}");
} }
private void parseConceptMapEM(Element result, FHIRLexer lexer) throws FHIRLexerException {
lexer.token("conceptmap");
Element map = Manager.build(worker, worker.fetchTypeDefinition("ConceptMap"));
Element eid = map.makeElement("id").markLocation(lexer.getCurrentLocation());
String id = lexer.readConstant("map id");
if (id.startsWith("#"))
throw lexer.error("Concept Map identifier must start with #");
eid.setValue(id);
map.makeElement("status").setValue(PublicationStatus.DRAFT.toCode()); // todo: how to add this to the text format
result.makeElement("contained").setElement("resource", map);
lexer.token("{");
// lexer.token("source");
// map.setSource(new UriType(lexer.readConstant("source")));
// lexer.token("target");
// map.setSource(new UriType(lexer.readConstant("target")));
Map<String, String> prefixes = new HashMap<String, String>();
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")) {
lexer.token("unmapped");
lexer.token("for");
String n = readPrefix(prefixes, lexer);
Element g = getGroupE(map, n, null);
lexer.token("=");
SourceLocation loc = lexer.getCurrentLocation();
String v = lexer.take();
if (v.equals("provided")) {
g.makeElement("unmapped").makeElement("mode").markLocation(loc).setValue(ConceptMapGroupUnmappedMode.USESOURCECODE.toCode());
} else
throw lexer.error("Only unmapped mode PROVIDED is supported at this time");
}
while (!lexer.hasToken("}")) {
String srcs = readPrefix(prefixes, lexer);
lexer.token(":");
SourceLocation scloc = lexer.getCurrentLocation();
String sc = lexer.getCurrent().startsWith("\"") ? lexer.readConstant("code") : lexer.take();
SourceLocation relLoc = lexer.getCurrentLocation();
ConceptMapRelationship rel = readRelationship(lexer);
String tgts = readPrefix(prefixes, lexer);
Element g = getGroupE(map, srcs, tgts);
Element e = g.addElement("element");
e.makeElement("code").markLocation(scloc).setValue(sc.startsWith("\"") ? lexer.processConstant(sc) : sc);
Element tgt = e.addElement("target");
tgt.makeElement("relationship").markLocation(relLoc).setValue(rel.toCode());
lexer.token(":");
tgt.makeElement("code").markLocation(lexer.getCurrentLocation()).setValue(lexer.getCurrent().startsWith("\"") ? lexer.readConstant("code") : lexer.take());
if (lexer.hasComments()) {
tgt.makeElement("comment").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
}
}
lexer.token("}");
}
private ConceptMapGroupComponent getGroup(ConceptMap map, String srcs, String tgts) { private ConceptMapGroupComponent getGroup(ConceptMap map, String srcs, String tgts) {
for (ConceptMapGroupComponent grp : map.getGroup()) { for (ConceptMapGroupComponent grp : map.getGroup()) {
if (grp.getSource().equals(srcs)) if (grp.getSource().equals(srcs))
@ -709,6 +794,23 @@ public class StructureMapUtilities {
return grp; return grp;
} }
private Element getGroupE(Element map, String srcs, String tgts) {
for (Element grp : map.getChildrenByName("group")) {
if (grp.getChildValue("source").equals(srcs)) {
Element tgt = grp.getNamedChild("target");
if (tgt == null || tgts == null || tgts.equals(tgt.getValue())) {
if (tgt == null && tgts != null)
grp.makeElement("target").setValue(tgts);
return grp;
}
}
}
Element grp = map.addElement("group");
grp.makeElement("source").setValue(srcs);
grp.makeElement("target").setValue(tgts);
return grp;
}
private String readPrefix(Map<String, String> prefixes, FHIRLexer lexer) throws FHIRLexerException { private String readPrefix(Map<String, String> prefixes, FHIRLexer lexer) throws FHIRLexerException {
String prefix = lexer.take(); String prefix = lexer.take();
@ -736,7 +838,6 @@ public class StructureMapUtilities {
private void parseUses(StructureMap result, FHIRLexer lexer) throws FHIRException { private void parseUses(StructureMap result, FHIRLexer lexer) throws FHIRException {
lexer.token("uses"); lexer.token("uses");
int currentLine = lexer.getCurrentLocation().getLine();
StructureMapStructureComponent st = result.addStructure(); StructureMapStructureComponent st = result.addStructure();
st.setUrl(lexer.readConstant("url")); st.setUrl(lexer.readConstant("url"));
if (lexer.hasToken("alias")) { if (lexer.hasToken("alias")) {
@ -749,12 +850,32 @@ public class StructureMapUtilities {
st.setDocumentation(lexer.getFirstComment()); st.setDocumentation(lexer.getFirstComment());
} }
private void parseUsesEM(Element result, FHIRLexer lexer) throws FHIRException {
lexer.token("uses");
Element st = result.addElement("structure");
st.makeElement("url").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("url"));
if (lexer.hasToken("alias")) {
lexer.token("alias");
st.makeElement("alias").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
lexer.token("as");
st.makeElement("mode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
lexer.skipToken(";");
if (lexer.hasComments()) {
st.makeElement("documentation").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
}
}
private void parseImports(StructureMap result, FHIRLexer lexer) throws FHIRException { private void parseImports(StructureMap result, FHIRLexer lexer) throws FHIRException {
lexer.token("imports"); lexer.token("imports");
int currentLine = lexer.getCurrentLocation().getLine();
result.addImport(lexer.readConstant("url")); result.addImport(lexer.readConstant("url"));
lexer.skipToken(";"); lexer.skipToken(";");
lexer.getFirstComment(); }
private void parseImportsEM(Element result, FHIRLexer lexer) throws FHIRException {
lexer.token("imports");
result.addElement("import").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("url"));
lexer.skipToken(";");
} }
private void parseGroup(StructureMap result, FHIRLexer lexer) throws FHIRException { private void parseGroup(StructureMap result, FHIRLexer lexer) throws FHIRException {
@ -829,6 +950,79 @@ public class StructureMapUtilities {
lexer.next(); lexer.next();
} }
private void parseGroupEM(Element result, FHIRLexer lexer) throws FHIRException {
SourceLocation commLoc = lexer.getCommentLocation();
String comment = lexer.getAllComments();
lexer.token("group");
Element group = result.addElement("group");
if (comment != null) {
group.makeElement("documentation").markLocation(commLoc).setValue(comment);
}
boolean newFmt = false;
if (lexer.hasToken("for")) {
lexer.token("for");
SourceLocation loc = lexer.getCurrentLocation();
if ("type".equals(lexer.getCurrent())) {
lexer.token("type");
lexer.token("+");
lexer.token("types");
group.makeElement("typeMode").markLocation(loc).setValue(StructureMapGroupTypeMode.TYPEANDTYPES.toCode());
} else {
lexer.token("types");
group.makeElement("typeMode").markLocation(loc).setValue(StructureMapGroupTypeMode.TYPES.toCode());
}
}
group.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
if (lexer.hasToken("(")) {
newFmt = true;
lexer.take();
while (!lexer.hasToken(")")) {
parseInputEM(group, lexer, true);
if (lexer.hasToken(","))
lexer.token(",");
}
lexer.take();
}
if (lexer.hasToken("extends")) {
lexer.next();
group.makeElement("extends").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
if (newFmt) {
if (lexer.hasToken("<")) {
lexer.token("<");
lexer.token("<");
if (lexer.hasToken("types")) {
group.makeElement("typeMode").markLocation(lexer.getCurrentLocation()).setValue(StructureMapGroupTypeMode.TYPES.toCode());
} else {
group.makeElement("typeMode").markLocation(lexer.getCurrentLocation()).setValue(StructureMapGroupTypeMode.TYPEANDTYPES.toCode());
lexer.token("type");
lexer.token("+");
}
lexer.token(">");
lexer.token(">");
}
lexer.token("{");
}
if (newFmt) {
while (!lexer.hasToken("}")) {
if (lexer.done())
throw lexer.error("premature termination expecting 'endgroup'");
parseRuleEM(result, group, lexer, true);
}
} else {
while (lexer.hasToken("input"))
parseInputEM(group, lexer, false);
while (!lexer.hasToken("endgroup")) {
if (lexer.done())
throw lexer.error("premature termination expecting 'endgroup'");
parseRuleEM(result, group, lexer, false);
}
}
lexer.next();
if (newFmt && lexer.hasToken(";"))
lexer.next();
}
private void parseInput(StructureMapGroupComponent group, FHIRLexer lexer, boolean newFmt) throws FHIRException { private void parseInput(StructureMapGroupComponent group, FHIRLexer lexer, boolean newFmt) throws FHIRException {
StructureMapGroupInputComponent input = group.addInput(); StructureMapGroupInputComponent input = group.addInput();
if (newFmt) { if (newFmt) {
@ -848,6 +1042,27 @@ public class StructureMapUtilities {
} }
} }
private void parseInputEM(Element group, FHIRLexer lexer, boolean newFmt) throws FHIRException {
Element input = group.addElement("input");
if (newFmt) {
input.makeElement("mode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
} else
lexer.token("input");
input.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
if (lexer.hasToken(":")) {
lexer.token(":");
input.makeElement("type").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
if (!newFmt) {
lexer.token("as");
input.makeElement("mode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
if (lexer.hasComments()) {
input.makeElement("documentation").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
}
lexer.skipToken(";");
}
}
private void parseRule(StructureMap map, List<StructureMapGroupRuleComponent> list, FHIRLexer lexer, boolean newFmt) throws FHIRException { private void parseRule(StructureMap map, List<StructureMapGroupRuleComponent> list, FHIRLexer lexer, boolean newFmt) throws FHIRException {
StructureMapGroupRuleComponent rule = new StructureMapGroupRuleComponent(); StructureMapGroupRuleComponent rule = new StructureMapGroupRuleComponent();
if (!newFmt) { if (!newFmt) {
@ -923,6 +1138,84 @@ public class StructureMapUtilities {
} }
} }
private void parseRuleEM(Element map, Element context, FHIRLexer lexer, boolean newFmt) throws FHIRException {
Element rule = context.addElement("rule");
if (!newFmt) {
rule.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.takeDottedToken());
lexer.token(":");
lexer.token("for");
} else {
if (lexer.hasComments()) {
rule.makeElement("documentation").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
}
}
boolean done = false;
while (!done) {
parseSourceEM(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) {
parseTargetEM(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");
parseRuleEM(map, rule, lexer, newFmt);
}
lexer.token("}");
} else {
done = false;
while (!done) {
parseRuleReferenceEM(rule, lexer);
done = !lexer.hasToken(",");
if (!done)
lexer.next();
}
}
}
if (!rule.hasChild("documentation") && lexer.hasComments()) {
rule.makeElement("documentation").markLocation(lexer.getCommentLocation()).setValue(lexer.getFirstComment());
}
if (isSimpleSyntaxEM(rule)) {
rule.forceElement("source").makeElement("variable").setValue(AUTO_VAR_NAME);
rule.forceElement("target").makeElement("variable").setValue(AUTO_VAR_NAME);
rule.forceElement("target").makeElement("transform").setValue(StructureMapTransform.CREATE.toCode());
// no dependencies - imply what is to be done based on types
}
if (newFmt) {
if (lexer.isConstant()) {
if (lexer.isStringConstant()) {
rule.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("ruleName"));
} else {
rule.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
} else {
if (rule.getChildrenByName("source").size() != 1 || !rule.getChildrenByName("source").get(0).hasChild("element"))
throw lexer.error("Complex rules must have an explicit name");
if (rule.getChildrenByName("source").get(0).hasChild("type"))
rule.makeElement("name").setValue(rule.getChildrenByName("source").get(0).getNamedChildValue("element") + "-" + rule.getChildrenByName("source").get(0).getNamedChildValue("type"));
else
rule.makeElement("name").setValue(rule.getChildrenByName("source").get(0).getNamedChildValue("element"));
}
lexer.token(";");
}
}
private boolean isSimpleSyntax(StructureMapGroupRuleComponent rule) { private boolean isSimpleSyntax(StructureMapGroupRuleComponent rule) {
return return
(rule.getSource().size() == 1 && rule.getSourceFirstRep().hasContext() && rule.getSourceFirstRep().hasElement() && !rule.getSourceFirstRep().hasVariable()) && (rule.getSource().size() == 1 && rule.getSourceFirstRep().hasContext() && rule.getSourceFirstRep().hasElement() && !rule.getSourceFirstRep().hasVariable()) &&
@ -930,6 +1223,15 @@ public class StructureMapUtilities {
(rule.getDependent().size() == 0 && rule.getRule().size() == 0); (rule.getDependent().size() == 0 && rule.getRule().size() == 0);
} }
private boolean isSimpleSyntaxEM(Element rule) {
return
(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")) &&
(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") &&
!rule.getChildren("target").get(0).hasChild("parameter")) &&
(rule.getChildren("dependent").size() == 0 && rule.getChildren("rule").size() == 0);
}
private void parseRuleReference(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRLexerException { private void parseRuleReference(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRLexerException {
StructureMapGroupRuleDependentComponent ref = rule.addDependent(); StructureMapGroupRuleDependentComponent ref = rule.addDependent();
ref.setName(lexer.take()); ref.setName(lexer.take());
@ -944,6 +1246,20 @@ public class StructureMapUtilities {
lexer.token(")"); lexer.token(")");
} }
private void parseRuleReferenceEM(Element rule, FHIRLexer lexer) throws FHIRLexerException {
Element ref = rule.addElement("dependent");
rule.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
lexer.token("(");
boolean done = false;
while (!done) {
parseParameterEM(ref, lexer);
done = !lexer.hasToken(",");
if (!done)
lexer.next();
}
lexer.token(")");
}
private void parseSource(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException { private void parseSource(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException {
StructureMapGroupRuleSourceComponent source = rule.addSource(); StructureMapGroupRuleSourceComponent source = rule.addSource();
source.setContext(lexer.take()); source.setContext(lexer.take());
@ -999,6 +1315,66 @@ public class StructureMapUtilities {
} }
} }
private void parseSourceEM(Element rule, FHIRLexer lexer) throws FHIRException {
Element source = rule.addElement("source");
source.makeElement("context").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
if (source.getChildValue("context").equals("search") && lexer.hasToken("(")) {
source.makeElement("context").markLocation(lexer.getCurrentLocation()).setValue("@search");
lexer.take();
SourceLocation loc = lexer.getCurrentLocation();
ExpressionNode node = fpe.parse(lexer);
source.setUserData(MAP_SEARCH_EXPRESSION, node);
source.makeElement("element").markLocation(loc).setValue(node.toString());
lexer.token(")");
} else if (lexer.hasToken(".")) {
lexer.token(".");
source.makeElement("element").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
if (lexer.hasToken(":")) {
// type and cardinality
lexer.token(":");
source.setType(lexer.takeDottedToken());
if (!lexer.hasToken("as", "first", "last", "not_first", "not_last", "only_one", "default")) {
source.makeElement("min").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
lexer.token("..");
source.makeElement("max").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
}
if (lexer.hasToken("default")) {
lexer.token("default");
source.makeElement("defaultValue").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("default value"));
}
if (Utilities.existsInList(lexer.getCurrent(), "first", "last", "not_first", "not_last", "only_one")) {
source.makeElement("listMode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
if (lexer.hasToken("as")) {
lexer.take();
source.makeElement("variable").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
if (lexer.hasToken("where")) {
lexer.take();
SourceLocation loc = lexer.getCurrentLocation();
ExpressionNode node = fpe.parse(lexer);
source.setUserData(MAP_WHERE_EXPRESSION, node);
source.makeElement("condition").markLocation(loc).setValue(node.toString());
}
if (lexer.hasToken("check")) {
lexer.take();
SourceLocation loc = lexer.getCurrentLocation();
ExpressionNode node = fpe.parse(lexer);
source.setUserData(MAP_WHERE_CHECK, node);
source.makeElement("check").markLocation(loc).setValue(node.toString());
}
if (lexer.hasToken("log")) {
lexer.take();
SourceLocation loc = lexer.getCurrentLocation();
ExpressionNode node = fpe.parse(lexer);
source.setUserData(MAP_WHERE_CHECK, node);
source.makeElement("logMessage").markLocation(loc).setValue(lexer.take());
}
}
private void parseTarget(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException { private void parseTarget(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException {
StructureMapGroupRuleTargetComponent target = rule.addTarget(); StructureMapGroupRuleTargetComponent target = rule.addTarget();
String start = lexer.take(); String start = lexer.take();
@ -1073,6 +1449,84 @@ public class StructureMapUtilities {
} }
} }
private void parseTargetEM(Element rule, FHIRLexer lexer) throws FHIRException {
Element target = rule.addElement("target");
SourceLocation loc = lexer.getCurrentLocation();
String start = lexer.take();
if (lexer.hasToken(".")) {
target.makeElement("context").markLocation(loc).setValue(start);
start = null;
lexer.token(".");
target.makeElement("element").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
String name;
boolean isConstant = false;
if (lexer.hasToken("=")) {
if (start != null) {
target.makeElement("context").markLocation(loc).setValue(start);
}
lexer.token("=");
isConstant = lexer.isConstant();
loc = lexer.getCurrentLocation();
name = lexer.take();
} else {
loc = lexer.getCurrentLocation();
name = start;
}
if ("(".equals(name)) {
// inline fluentpath expression
target.makeElement("transform").markLocation(lexer.getCurrentLocation()).setValue(StructureMapTransform.EVALUATE.toCode());
loc = lexer.getCurrentLocation();
ExpressionNode node = fpe.parse(lexer);
target.setUserData(MAP_EXPRESSION, node);
target.addElement("parameter").markLocation(loc).setValue(node.toString());
lexer.token(")");
} else if (lexer.hasToken("(")) {
target.makeElement("transform").markLocation(loc).setValue(name);
lexer.token("(");
if (target.getChildValue("transform").equals(StructureMapTransform.EVALUATE.toCode())) {
parseParameterEM(target, lexer);
lexer.token(",");
loc = lexer.getCurrentLocation();
ExpressionNode node = fpe.parse(lexer);
target.setUserData(MAP_EXPRESSION, node);
target.addElement("parameter").markLocation(loc).setValue(node.toString());
} else {
while (!lexer.hasToken(")")) {
parseParameterEM(target, lexer);
if (!lexer.hasToken(")"))
lexer.token(",");
}
}
lexer.token(")");
} else if (name != null) {
target.makeElement("transform").markLocation(loc).setValue(StructureMapTransform.COPY.toCode());
if (!isConstant) {
loc = lexer.getCurrentLocation();
String id = name;
while (lexer.hasToken(".")) {
id = id + lexer.take() + lexer.take();
}
target.addElement("parameter").markLocation(loc).setValue(id);
} else {
target.addElement("parameter").markLocation(lexer.getCurrentLocation()).setValue(readConstantEM(name, lexer));
}
}
if (lexer.hasToken("as")) {
lexer.take();
target.makeElement("variable").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
while (Utilities.existsInList(lexer.getCurrent(), "first", "last", "share", "collate")) {
if (lexer.getCurrent().equals("share")) {
target.makeElement("listMode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
target.makeElement("listRuleId").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
} else {
target.makeElement("listMode").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
}
}
}
private void parseParameter(StructureMapGroupRuleDependentComponent ref, FHIRLexer lexer) throws FHIRLexerException, FHIRFormatError { private void parseParameter(StructureMapGroupRuleDependentComponent ref, FHIRLexer lexer) throws FHIRLexerException, FHIRFormatError {
if (!lexer.isConstant()) { if (!lexer.isConstant()) {
@ -1094,6 +1548,16 @@ public class StructureMapUtilities {
} }
} }
private void parseParameterEM(Element ref, FHIRLexer lexer) throws FHIRLexerException, FHIRFormatError {
if (!lexer.isConstant()) {
ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
} else if (lexer.isStringConstant())
ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("??"));
else {
ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).setValue(readConstantEM(lexer.take(), lexer));
}
}
private DataType readConstant(String s, FHIRLexer lexer) throws FHIRLexerException { private DataType readConstant(String s, FHIRLexer lexer) throws FHIRLexerException {
if (Utilities.isInteger(s)) if (Utilities.isInteger(s))
return new IntegerType(s); return new IntegerType(s);
@ -1105,6 +1569,17 @@ public class StructureMapUtilities {
return new StringType(lexer.processConstant(s)); return new StringType(lexer.processConstant(s));
} }
private String readConstantEM(String s, FHIRLexer lexer) throws FHIRLexerException {
if (Utilities.isInteger(s))
return s;
else if (Utilities.isDecimal(s, false))
return s;
else if (Utilities.existsInList(s, "true", "false"))
return s;
else
return lexer.processConstant(s);
}
public StructureDefinition getTargetType(StructureMap map) throws FHIRException { public StructureDefinition getTargetType(StructureMap map) throws FHIRException {
boolean found = false; boolean found = false;
StructureDefinition res = null; StructureDefinition res = null;

View File

@ -92,6 +92,22 @@ public class StructureMapUtilitiesTest implements ITransformerServices {
} }
@Test
public void testSyntaxEM() throws IOException, FHIRException {
StructureMapUtilities scu = new StructureMapUtilities(context, this);
String fileMap = TestingUtilities.loadTestResource("r5", "structure-mapping", "syntax.map");
System.out.println(fileMap);
Element structureMap = scu.parseEM(fileMap, "Syntax");
// assertSerializeDeserialize(structureMap);
//
// String renderedMap = StructureMapUtilities.render(structureMap);
// StructureMap map = scu.parse(renderedMap, "Syntax");
// System.out.println(map);
// assertSerializeDeserialize(map);
}
@Override @Override
public void log(String message) { public void log(String message) {