diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java index 65b1ab300..87d595579 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java @@ -112,6 +112,7 @@ public abstract class BaseLoaderR5 implements IContextResourceLoader { // and we only patch URLs to support version transforms // so we just patch sd/od -> vs -> cs protected void doPatchUrls(Resource resource) { + resource.setUserData("old.load.mode", true); if (resource instanceof CanonicalResource) { CanonicalResource cr = (CanonicalResource) resource; cr.setUrl(patchUrl(cr.getUrl(), cr.fhirType())); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index 5e0892f0c..743ec1427 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -570,7 +570,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon public List getResourceNames() { Set result = new HashSet(); for (StructureDefinition sd : listStructures()) { - if (sd.getKind() == StructureDefinitionKind.RESOURCE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) + if (sd.getKind() == StructureDefinitionKind.RESOURCE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && !sd.hasUserData("old.load.mode")) result.add(sd.getName()); } return Utilities.sorted(result); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index 97d851c94..327bf0d05 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -61,6 +61,7 @@ import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.MergedList; import org.hl7.fhir.utilities.MergedList.MergeNode; import org.hl7.fhir.utilities.SourceLocation; @@ -4173,6 +4174,10 @@ public class FHIRPathEngine { result.add(new StringType(Utilities.escapeXml(cnt))); } else if ("json".equals(param)) { result.add(new StringType(Utilities.escapeJson(cnt))); + } else if ("url".equals(param)) { + result.add(new StringType(Utilities.URLEncode(cnt))); + } else if ("md".equals(param)) { + result.add(new StringType(MarkDownProcessor.makeStringSafeAsMarkdown(cnt))); } } @@ -4190,6 +4195,10 @@ public class FHIRPathEngine { result.add(new StringType(Utilities.unescapeXml(cnt))); } else if ("json".equals(param)) { result.add(new StringType(Utilities.unescapeJson(cnt))); + } else if ("url".equals(param)) { + result.add(new StringType(Utilities.URLDecode(cnt))); + } else if ("md".equals(param)) { + result.add(new StringType(MarkDownProcessor.makeMarkdownForString(cnt))); } } @@ -4222,9 +4231,14 @@ public class FHIRPathEngine { private List funcJoin(ExecutionContext context, List focus, ExpressionNode exp) { List nl = execute(context, focus, exp.getParameters().get(0), true); String param = nl.get(0).primitiveValue(); - + String param2 = param; + if (exp.getParameters().size() == 2) { + nl = execute(context, focus, exp.getParameters().get(1), true); + param2 = nl.get(0).primitiveValue(); + } + List result = new ArrayList(); - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(param); + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(param, param2); for (Base i : focus) { b.append(i.primitiveValue()); } 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 fcf608def..9f66c0e50 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 @@ -106,7 +106,7 @@ public class StructureMapUtilities { private static final boolean RENDER_MULTIPLE_TARGETS_ONELINE = true; public static final String AUTO_VAR_NAME = "vvv"; public static final String DEF_GROUP_NAME = "DefaultMappingGroupAnonymousAlias"; - + private final IWorkerContext worker; private final FHIRPathEngine fpe; private ITransformerServices services; @@ -658,6 +658,9 @@ public class StructureMapUtilities { case "title" : result.setTitle(lexer.readConstant("title")); break; + case "status" : + result.setStatus(PublicationStatus.fromCode(lexer.readConstant("status"))); + break; default: lexer.readConstant("nothing"); // nothing @@ -666,7 +669,7 @@ public class StructureMapUtilities { } if (!result.hasId() && result.hasName()) { String id = Utilities.makeId(result.getName()); - if (Utilities.noString(id)) { + if (!Utilities.noString(id)) { result.setId(id); } } @@ -2697,4 +2700,65 @@ public class StructureMapUtilities { this.exceptionsForChecks = exceptionsForChecks; } + public List getMapsForUrl(List maps, String url, StructureMapInputMode mode) { + List res = new ArrayList<>(); + for (StructureMap map : maps) { + if (mapIsForUrl(map, url, mode)) { + res.add(map); + } + } + return res; + } + + private boolean mapIsForUrl(StructureMap map, String url, StructureMapInputMode mode) { + for (StructureMapGroupComponent grp : map.getGroup()) { + if (grp.getTypeMode() != StructureMapGroupTypeMode.NULL) { + for (StructureMapGroupInputComponent p : grp.getInput()) { + if (mode == null || mode == p.getMode()) { + String t = resolveInputType(p, map); + if (url.equals(t)) { + return true; + } + } + } + } + } + return false; + } + + public List getMapsForUrlPrefix(List maps, String url, StructureMapInputMode mode) { + List res = new ArrayList<>(); + for (StructureMap map : maps) { + if (mapIsForUrlPrefix(map, url, mode)) { + res.add(map); + } + } + return res; + } + + private boolean mapIsForUrlPrefix(StructureMap map, String url, StructureMapInputMode mode) { + for (StructureMapGroupComponent grp : map.getGroup()) { + if (grp.getTypeMode() != StructureMapGroupTypeMode.NULL) { + for (StructureMapGroupInputComponent p : grp.getInput()) { + if (mode == null || mode == p.getMode()) { + String t = resolveInputType(p, map); + if (t != null && t.startsWith(url)) { + return true; + } + } + } + } + } + return false; + } + + private String resolveInputType(StructureMapGroupInputComponent p, StructureMap map) { + for (StructureMapStructureComponent struc : map.getStructure()) { + if (struc.hasAlias() && struc.getAlias().equals(p.getType())) { + return struc.getUrl(); + } + } + return null; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index b29ecd246..0cb5e1257 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -1980,6 +1980,20 @@ public class Utilities { } } + public static String tail(String url) { + int i = url.length()-1; + while (i >= 0 && isTokenChar(url.charAt(i))) { + i--; + } + if (i < 0) { + return url; + } else { + return url.substring(i+1); + } + } + + + //public static boolean !isWhitespace(String s) { //boolean ok = true; //for (int i = 0; i < s.length(); i++) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/R4R5MapTester.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/R4R5MapTester.java new file mode 100644 index 000000000..cff739b6a --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/R4R5MapTester.java @@ -0,0 +1,110 @@ +package org.hl7.fhir.validation.special; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.StructureMap; +import org.hl7.fhir.r5.model.StructureMap.StructureMapInputMode; +import org.hl7.fhir.r5.context.SimpleWorkerContext; +import org.hl7.fhir.r5.context.SimpleWorkerContext.SimpleWorkerContextBuilder; +import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; +import org.hl7.fhir.r5.utils.structuremap.VariableMode; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.json.JsonException; +import org.hl7.fhir.utilities.json.model.JsonObject; +import org.hl7.fhir.utilities.json.model.JsonProperty; +import org.hl7.fhir.utilities.json.parser.JsonParser; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; + +public class R4R5MapTester { + + private SimpleWorkerContext context; + private FilesystemPackageCacheManager pcm; + private StructureMapUtilities utils; + private List allMaps; + + public static void main(String[] args) throws JsonException, IOException { + + // arg[0] is the location of the fhir-extensions repo + new R4R5MapTester().testMaps(args[0]); + } + + private void testMaps(String src) throws JsonException, IOException { + log("Load Test Outcomes"); + JsonObject json = JsonParser.parseObjectFromFile(Utilities.path(src, "input", "_data", "conversions.json")); + log("Load R5"); + pcm = new FilesystemPackageCacheManager(true); + context = new SimpleWorkerContextBuilder().withAllowLoadingDuplicates(true).fromPackage(pcm.loadPackage("hl7.fhir.r5.core#current")); + log("Load Maps"); + context.loadFromPackage(pcm.loadPackage("hl7.fhir.uv.extensions#dev"), null); + utils = new StructureMapUtilities(context); + allMaps = context.fetchResourcesByType(StructureMap.class); + + log("Resource Count = "+context.getResourceNames().size()); + log("Map Count = "+allMaps.size()); + boolean changed = false; + for (JsonProperty jp : json.getProperties()) { + String rn = jp.getName(); + log(" "+rn); + JsonObject o = json.getJsonObject(rn); + StructureDefinition sd = context.fetchTypeDefinition(rn); + List mapSrc = utils.getMapsForUrl(allMaps, sd.getUrl(), StructureMapInputMode.SOURCE); + List mapTgt = utils.getMapsForUrl(allMaps, sd.getUrl(), StructureMapInputMode.TARGET); + changed = checkMaps(rn, o.getJsonObject("r4"), "http://hl7.org/fhir/4.0", mapSrc, mapTgt) || changed; + changed = checkMaps(rn, o.getJsonObject("r4b"), "http://hl7.org/fhir/4.0", mapSrc, mapTgt) || changed; + } + if (changed) { + JsonParser.compose(json, new FileOutputStream(Utilities.path(src, "input", "_data", "conversions.json")), true); + } +// load R4 +// load R4B +// load the maps +// load the existing outcomes +// +// for R4: +// fetch the examples package +// for each R5 resource type +// find the map +// get the source name +// iterate the examples +// find the applicable map +// do the conversion +// write outcome + + } + + private boolean checkMaps(String rn, JsonObject json, String ns, List mapSrc, List mapTgt) { + List src = utils.getMapsForUrlPrefix(mapSrc, ns, StructureMapInputMode.TARGET); + List tgt = utils.getMapsForUrlPrefix(mapTgt, ns, StructureMapInputMode.SOURCE); + if (src.size() + tgt.size() == 0) { + json.set("status", "No Maps Defined"); + json.set("testColor", "#dddddd"); + json.set("testMessage", "--"); + } else { + boolean isDraft = false; + for (StructureMap map : src) { + isDraft = map.getStatus() == PublicationStatus.DRAFT || isDraft; + } + for (StructureMap map : tgt) { + isDraft = map.getStatus() == PublicationStatus.DRAFT || isDraft; + } + json.set("status", ""+(src.size()+tgt.size())+" Maps Defined"+(isDraft ? " (draft)" : "")); + if (context.getResourceNames().contains(rn)) { + json.set("testColor", "#ffcccc"); + json.set("testMessage", "To Do"); + } else { + json.set("testColor", "#eeeeee"); + json.set("testMessage", "n/a"); + } + } + return true; + } + + private void log(String msg) { + System.out.println(msg); + } + +}