Merge pull request #1139 from hapifhir/gg-202302-map-validation-2

Work on StructureMap validation
This commit is contained in:
Grahame Grieve 2023-02-27 09:14:13 +11:00 committed by GitHub
commit a62c8683f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 476 additions and 259 deletions

View File

@ -3,8 +3,21 @@ package org.hl7.fhir.convertors.loaders.loaderR5;
import java.io.IOException; import java.io.IOException;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.context.IWorkerContext.IContextResourceLoader; import org.hl7.fhir.r5.context.IWorkerContext.IContextResourceLoader;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.OperationDefinition;
import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionParameterComponent;
import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.npm.NpmPackage;
@ -18,12 +31,8 @@ import lombok.experimental.Accessors;
public abstract class BaseLoaderR5 implements IContextResourceLoader { public abstract class BaseLoaderR5 implements IContextResourceLoader {
protected final String URL_BASE = "http://hl7.org/fhir/"; protected final String URL_BASE = "http://hl7.org/fhir/";
protected final String URL_DSTU2 = "http://hl7.org/fhir/1.0/";
protected final String URL_DSTU2016MAY = "http://hl7.org/fhir/1.4/";
protected final String URL_DSTU3 = "http://hl7.org/fhir/3.0/";
protected final String URL_R4 = "http://hl7.org/fhir/4.0/";
protected final String URL_ELEMENT_DEF_NAMESPACE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"; protected final String URL_ELEMENT_DEF_NAMESPACE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace";
@Getter @Setter protected boolean patchUrls; protected boolean patchUrls;
@Getter @Setter protected boolean killPrimitives; @Getter @Setter protected boolean killPrimitives;
@Getter protected String[] types; @Getter protected String[] types;
protected ILoaderKnowledgeProviderR5 lkp; protected ILoaderKnowledgeProviderR5 lkp;
@ -73,4 +82,83 @@ public abstract class BaseLoaderR5 implements IContextResourceLoader {
} }
} }
public boolean isPatchUrls() {
return patchUrls;
}
public void setPatchUrls(boolean patchUrls) {
this.patchUrls = patchUrls;
}
protected abstract String versionString();
@Override
public String patchUrl(String url, String type) {
if (!patchUrls || url == null) {
return url;
} else if (url.startsWith("http://hl7.org/fhir/"+type+"/")) {
return "http://hl7.org/fhir/"+versionString()+"/"+url.substring(20);
} else if ("CodeSystem".equals(type) && url.startsWith("http://hl7.org/fhir/")) {
return "http://hl7.org/fhir/"+versionString()+"/"+url.substring(20);
} else {
return url;
}
}
// we don't patch everything. It's quite hard work to do that,
// and we only patch URLs to support version transforms
// so we just patch sd/od -> vs -> cs
protected void doPatchUrls(Resource resource) {
if (resource instanceof CanonicalResource) {
CanonicalResource cr = (CanonicalResource) resource;
cr.setUrl(patchUrl(cr.getUrl(), cr.fhirType()));
if (cr instanceof StructureDefinition) {
StructureDefinition sd = (StructureDefinition) cr;
new ProfileUtilities(null, null, null, null).setIds(sd, false);
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
}
if (cr instanceof ValueSet) {
ValueSet vs = (ValueSet) cr;
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
inc.setSystem(patchUrl(inc.getSystem(), "CodeSystem"));
}
for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
inc.setSystem(patchUrl(inc.getSystem(), "CodeSystem"));
}
}
if (cr instanceof OperationDefinition) {
OperationDefinition od = (OperationDefinition) cr;
for (OperationDefinitionParameterComponent param : od.getParameter()) {
patchUrls(param);
}
}
}
}
private void patchUrls(OperationDefinitionParameterComponent param) {
if (param.hasBinding()) {
param.getBinding().setValueSet(patchUrl(param.getBinding().getValueSet(), "ValueSet"));
}
for (OperationDefinitionParameterComponent p : param.getPart()) {
patchUrls(p);
}
}
private void patchUrl(ElementDefinition ed) {
for (TypeRefComponent tr : ed.getType()) {
for (CanonicalType s : tr.getTargetProfile()) {
s.setValue(patchUrl(s.getValue(), "StructureDefinitino"));
}
}
if (ed.hasBinding()) {
ed.getBinding().setValueSet(patchUrl(ed.getBinding().getValueSet(), "ValueSet"));
}
}
} }

View File

@ -98,13 +98,10 @@ public class R2016MayToR5Loader extends BaseLoaderR5 {
} }
b.getEntry().removeAll(remove); b.getEntry().removeAll(remove);
} }
for (BundleEntryComponent be : b.getEntry()) { if (patchUrls) {
if (be.hasResource() && be.getResource() instanceof StructureDefinition) { for (BundleEntryComponent be : b.getEntry()) {
StructureDefinition sd = (StructureDefinition) be.getResource(); if (be.hasResource()) {
new ProfileUtilities(null, null, null, null).setIds(sd, false); doPatchUrls(be.getResource());
if (patchUrls) {
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_DSTU2016MAY));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
} }
} }
} }
@ -128,33 +125,20 @@ public class R2016MayToR5Loader extends BaseLoaderR5 {
throw new FHIRException("Cannot kill primitives when using deferred loading"); throw new FHIRException("Cannot kill primitives when using deferred loading");
} }
if (patchUrls) { if (patchUrls) {
if (r5 instanceof StructureDefinition) { doPatchUrls(r5);
StructureDefinition sd = (StructureDefinition) r5;
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_R4));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
}
} }
return r5; return r5;
} }
private void patchUrl(ElementDefinition ed) {
for (TypeRefComponent tr : ed.getType()) {
for (CanonicalType s : tr.getTargetProfile()) {
s.setValue(s.getValue().replace(URL_BASE, URL_DSTU2016MAY));
}
for (CanonicalType s : tr.getProfile()) {
s.setValue(s.getValue().replace(URL_BASE, URL_DSTU2016MAY));
}
}
}
@Override @Override
public List<CodeSystem> getCodeSystems() { public List<CodeSystem> getCodeSystems() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override
protected String versionString() {
return "4.3";
}
} }

View File

@ -102,10 +102,8 @@ public class R2ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
} }
if (patchUrls) { if (patchUrls) {
for (BundleEntryComponent be : b.getEntry()) { for (BundleEntryComponent be : b.getEntry()) {
if (be.hasResource() && be.getResource() instanceof StructureDefinition) { if (be.hasResource()) {
StructureDefinition sd = (StructureDefinition) be.getResource(); doPatchUrls(be.getResource());
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_DSTU2));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
} }
} }
} }
@ -126,30 +124,11 @@ public class R2ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
throw new FHIRException("Cannot kill primitives when using deferred loading"); throw new FHIRException("Cannot kill primitives when using deferred loading");
} }
if (patchUrls) { if (patchUrls) {
if (r5 instanceof StructureDefinition) { doPatchUrls(r5);
StructureDefinition sd = (StructureDefinition) r5;
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_R4));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
}
} }
return r5; return r5;
} }
private void patchUrl(ElementDefinition ed) {
for (TypeRefComponent tr : ed.getType()) {
for (CanonicalType s : tr.getTargetProfile()) {
s.setValue(s.getValue().replace(URL_BASE, URL_DSTU2));
}
for (CanonicalType s : tr.getProfile()) {
s.setValue(s.getValue().replace(URL_BASE, URL_DSTU2));
}
}
}
@Override @Override
public List<CodeSystem> getCodeSystems() { public List<CodeSystem> getCodeSystems() {
List<CodeSystem> list = new ArrayList<>(); List<CodeSystem> list = new ArrayList<>();
@ -160,4 +139,9 @@ public class R2ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
return list; return list;
} }
@Override
protected String versionString() {
return "1.0";
}
} }

View File

@ -99,14 +99,8 @@ public class R3ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
} }
if (patchUrls) { if (patchUrls) {
for (BundleEntryComponent be : b.getEntry()) { for (BundleEntryComponent be : b.getEntry()) {
if (be.hasResource() && be.getResource() instanceof StructureDefinition) { if (be.hasResource()) {
StructureDefinition sd = (StructureDefinition) be.getResource(); doPatchUrls(be.getResource());
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_DSTU3));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
} }
} }
} }
@ -130,33 +124,19 @@ public class R3ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
throw new FHIRException("Cannot kill primitives when using deferred loading"); throw new FHIRException("Cannot kill primitives when using deferred loading");
} }
if (patchUrls) { if (patchUrls) {
if (r5 instanceof StructureDefinition) { doPatchUrls(r5);
StructureDefinition sd = (StructureDefinition) r5;
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_R4));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
}
} }
return r5; return r5;
} }
private void patchUrl(ElementDefinition ed) {
for (TypeRefComponent tr : ed.getType()) {
for (CanonicalType s : tr.getTargetProfile()) {
s.setValue(s.getValue().replace(URL_BASE, URL_DSTU3));
}
for (CanonicalType s : tr.getProfile()) {
s.setValue(s.getValue().replace(URL_BASE, URL_DSTU3));
}
}
}
@Override @Override
public List<CodeSystem> getCodeSystems() { public List<CodeSystem> getCodeSystems() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override
protected String versionString() {
return "3.0";
}
} }

View File

@ -103,14 +103,8 @@ public class R4BToR5Loader extends BaseLoaderR5 implements IContextResourceLoade
} }
if (patchUrls) { if (patchUrls) {
for (BundleEntryComponent be : b.getEntry()) { for (BundleEntryComponent be : b.getEntry()) {
if (be.hasResource() && be.getResource() instanceof StructureDefinition) { if (be.hasResource()) {
StructureDefinition sd = (StructureDefinition) be.getResource(); doPatchUrls(be.getResource());
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_R4));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
} }
} }
} }
@ -137,34 +131,20 @@ public class R4BToR5Loader extends BaseLoaderR5 implements IContextResourceLoade
r5 = new StructureDefinitionHacker(version).fixSD((StructureDefinition) r5); r5 = new StructureDefinitionHacker(version).fixSD((StructureDefinition) r5);
} }
if (patchUrls) { if (patchUrls) {
if (r5 instanceof StructureDefinition) { doPatchUrls(r5);
StructureDefinition sd = (StructureDefinition) r5;
sd.setUrl(sd.getUrl().replace(URL_BASE, "http://hl7.org/fhir/4.0/"));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType("http://hl7.org/fhir"));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
}
} }
return r5; return r5;
} }
private void patchUrl(ElementDefinition ed) {
for (TypeRefComponent tr : ed.getType()) {
for (CanonicalType s : tr.getTargetProfile()) {
s.setValue(s.getValue().replace(URL_BASE, "http://hl7.org/fhir/4.0/"));
}
for (CanonicalType s : tr.getProfile()) {
s.setValue(s.getValue().replace(URL_BASE, "http://hl7.org/fhir/4.0/"));
}
}
}
@Override @Override
public List<CodeSystem> getCodeSystems() { public List<CodeSystem> getCodeSystems() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override
protected String versionString() {
return "4.3";
}
} }

View File

@ -103,14 +103,8 @@ public class R4ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
} }
if (patchUrls) { if (patchUrls) {
for (BundleEntryComponent be : b.getEntry()) { for (BundleEntryComponent be : b.getEntry()) {
if (be.hasResource() && be.getResource() instanceof StructureDefinition) { if (be.hasResource()) {
StructureDefinition sd = (StructureDefinition) be.getResource(); doPatchUrls(be.getResource());
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_R4));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
} }
} }
} }
@ -137,34 +131,21 @@ public class R4ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
r5 = new StructureDefinitionHacker(version).fixSD((StructureDefinition) r5); r5 = new StructureDefinitionHacker(version).fixSD((StructureDefinition) r5);
} }
if (patchUrls) { if (patchUrls) {
if (r5 instanceof StructureDefinition) { doPatchUrls(r5);
StructureDefinition sd = (StructureDefinition) r5;
sd.setUrl(sd.getUrl().replace(URL_BASE, "http://hl7.org/fhir/4.0/"));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType("http://hl7.org/fhir"));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
}
} }
return r5; return r5;
} }
private void patchUrl(ElementDefinition ed) {
for (TypeRefComponent tr : ed.getType()) {
for (CanonicalType s : tr.getTargetProfile()) {
s.setValue(s.getValue().replace(URL_BASE, "http://hl7.org/fhir/4.0/"));
}
for (CanonicalType s : tr.getProfile()) {
s.setValue(s.getValue().replace(URL_BASE, "http://hl7.org/fhir/4.0/"));
}
}
}
@Override @Override
public List<CodeSystem> getCodeSystems() { public List<CodeSystem> getCodeSystems() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override
protected String versionString() {
return "4.0";
}
} }

View File

@ -96,14 +96,8 @@ public class R5ToR5Loader extends BaseLoaderR5 {
} }
if (patchUrls) { if (patchUrls) {
for (BundleEntryComponent be : b.getEntry()) { for (BundleEntryComponent be : b.getEntry()) {
if (be.hasResource() && be.getResource() instanceof StructureDefinition) { if (be.hasResource()) {
StructureDefinition sd = (StructureDefinition) be.getResource(); doPatchUrls(be.getResource());
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_R4));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
} }
} }
} }
@ -126,34 +120,20 @@ public class R5ToR5Loader extends BaseLoaderR5 {
throw new FHIRException("Cannot kill primitives when using deferred loading"); throw new FHIRException("Cannot kill primitives when using deferred loading");
} }
if (patchUrls) { if (patchUrls) {
if (r5 instanceof StructureDefinition) { doPatchUrls(r5);
StructureDefinition sd = (StructureDefinition) r5;
sd.setUrl(sd.getUrl().replace(URL_BASE, URL_R4));
sd.addExtension().setUrl(URL_ELEMENT_DEF_NAMESPACE).setValue(new UriType(URL_BASE));
for (ElementDefinition ed : sd.getSnapshot().getElement())
patchUrl(ed);
for (ElementDefinition ed : sd.getDifferential().getElement())
patchUrl(ed);
}
} }
return r5; return r5;
} }
private void patchUrl(ElementDefinition ed) {
for (TypeRefComponent tr : ed.getType()) {
for (CanonicalType s : tr.getTargetProfile()) {
s.setValue(s.getValue().replace(URL_BASE, URL_R4));
}
for (CanonicalType s : tr.getProfile()) {
s.setValue(s.getValue().replace(URL_BASE, URL_R4));
}
}
}
@Override @Override
public List<CodeSystem> getCodeSystems() { public List<CodeSystem> getCodeSystems() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override
protected String versionString() {
return "5.0";
}
} }

View File

@ -265,8 +265,10 @@ public class StructureMapUtilities {
public StructureMapUtilities(IWorkerContext worker) { public StructureMapUtilities(IWorkerContext worker) {
super(); super();
this.worker = worker; this.worker = worker;
fpe = new FHIRPathEngine(worker); if (worker != null) {
fpe.setHostServices(new FFHIRPathHostServices()); fpe = new FHIRPathEngine(worker);
fpe.setHostServices(new FFHIRPathHostServices());
}
} }
public static String render(StructureMap map) { public static String render(StructureMap map) {

View File

@ -23,6 +23,7 @@ import org.hl7.fhir.r5.model.NamingSystem.NamingSystemIdentifierType;
import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent; import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.StructureMap;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.r5.model.Identifier; import org.hl7.fhir.r5.model.Identifier;
@ -390,5 +391,18 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
return concreteResourceNames; return concreteResourceNames;
} }
public List<StructureMap> listMaps(String url) {
List<StructureMap> res = new ArrayList<>();
String start = url.substring(0, url.indexOf("*"));
String end = url.substring(url.indexOf("*")+1);
for (StructureMap map : context.fetchResourcesByType(StructureMap.class)) {
String u = map.getUrl();
if (u.startsWith(start) && u.endsWith(end)) {
res.add(map);
}
}
return res;
}
} }

View File

@ -321,6 +321,22 @@ public interface IWorkerContext {
* @return * @return
*/ */
List<CodeSystem> getCodeSystems(); List<CodeSystem> getCodeSystems();
/**
* if this is true, then the loader will patch canonical URLs and cross-links
* to add /X.X/ into the URL so that different versions can be loaded safely
*
* default is false
*/
void setPatchUrls(boolean value);
/**
* patch the URL if necessary
*
* @param url
* @return
*/
String patchUrl(String url, String resourceType);
} }
/** /**

View File

@ -97,7 +97,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
private final IContextResourceLoader loader; private final IContextResourceLoader loader;
public PackageResourceLoader(PackageResourceInformation pri, IContextResourceLoader loader) { public PackageResourceLoader(PackageResourceInformation pri, IContextResourceLoader loader) {
super(pri.getResourceType(), pri.getId(), pri.getUrl(),pri.getVersion()); super(pri.getResourceType(), pri.getId(), loader == null ? pri.getUrl() :loader.patchUrl(pri.getUrl(), pri.getResourceType()), pri.getVersion());
this.filename = pri.getFilename(); this.filename = pri.getFilename();
this.loader = loader; this.loader = loader;
} }

View File

@ -55,18 +55,29 @@ public class FmlParser extends ParserBase {
} }
public Element parse(String text) throws FHIRException { public Element parse(String text) throws FHIRException {
FHIRLexer lexer = new FHIRLexer(text, "source"); FHIRLexer lexer = new FHIRLexer(text, "source", true);
if (lexer.done()) if (lexer.done())
throw lexer.error("Map Input cannot be empty"); throw lexer.error("Map Input cannot be empty");
lexer.token("map");
Element result = Manager.build(context, context.fetchTypeDefinition("StructureMap")); Element result = Manager.build(context, context.fetchTypeDefinition("StructureMap"));
try { try {
result.makeElement("url").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("url")); if (lexer.hasToken("map")) {
lexer.token("="); lexer.token("map");
result.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("name")); result.makeElement("url").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("url"));
if (lexer.hasComments()) { lexer.token("=");
result.makeElement("description").markLocation(lexer.getCurrentLocation()).setValue(lexer.getAllComments()); result.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.readConstant("name"));
if (lexer.hasComments()) {
result.makeElement("description").markLocation(lexer.getCurrentLocation()).setValue(lexer.getAllComments());
}
} else {
while (lexer.hasToken("///")) {
lexer.next();
String fid = lexer.takeDottedToken();
Element e = result.makeElement(fid).markLocation(lexer.getCurrentLocation());
lexer.token("=");
e.setValue(lexer.readConstant("meta value"));
}
} }
lexer.setMetadataFormat(false);
while (lexer.hasToken("conceptmap")) while (lexer.hasToken("conceptmap"))
parseConceptMap(result, lexer); parseConceptMap(result, lexer);
@ -366,7 +377,7 @@ public class FmlParser extends ParserBase {
private void parseRuleReference(Element rule, FHIRLexer lexer) throws FHIRLexerException { private void parseRuleReference(Element rule, FHIRLexer lexer) throws FHIRLexerException {
Element ref = rule.addElement("dependent"); Element ref = rule.addElement("dependent");
rule.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take()); ref.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take());
lexer.token("("); lexer.token("(");
boolean done = false; boolean done = false;
while (!done) { while (!done) {

View File

@ -52,4 +52,14 @@ public class TestPackageLoader implements IContextResourceLoader {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override
public void setPatchUrls(boolean value) {
}
@Override
public String patchUrl(String url, String resourceType) {
return url;
}
} }

View File

@ -89,6 +89,7 @@ public class FHIRLexer {
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; private SourceLocation commentLocation;
private boolean metadataFormat;
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;
@ -102,6 +103,13 @@ public class FHIRLexer {
currentLocation = new SourceLocation(1, 1); currentLocation = new SourceLocation(1, 1);
next(); next();
} }
public FHIRLexer(String source, String name, boolean metadataFormat) throws FHIRLexerException {
this.source = source == null ? "" : source;
this.name = name == null ? "??" : name;
this.metadataFormat = metadataFormat;
currentLocation = new SourceLocation(1, 1);
next();
}
public String getCurrent() { public String getCurrent() {
return current; return current;
} }
@ -211,10 +219,13 @@ public class FHIRLexer {
} else if (ch == '/') { } else if (ch == '/') {
cursor++; cursor++;
if (cursor < source.length() && (source.charAt(cursor) == '/')) { if (cursor < source.length() && (source.charAt(cursor) == '/')) {
// this is en error - should already have been skipped // we've run into metadata
error("This shouldn't happen?"); cursor++;
cursor++;
current = source.substring(currentStart, cursor);
} else {
current = source.substring(currentStart, cursor);
} }
current = source.substring(currentStart, cursor);
} else if (ch == '$') { } else if (ch == '$') {
cursor++; cursor++;
while (cursor < source.length() && (source.charAt(cursor) >= 'a' && source.charAt(cursor) <= 'z')) while (cursor < source.length() && (source.charAt(cursor) >= 'a' && source.charAt(cursor) <= 'z'))
@ -309,7 +320,7 @@ public class FHIRLexer {
boolean last13 = false; boolean last13 = false;
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)) && !isMetadataStart()) {
commentLocation = currentLocation; 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')) {
@ -338,6 +349,10 @@ public class FHIRLexer {
} }
} }
private boolean isMetadataStart() {
return metadataFormat && cursor < source.length() - 2 && "///".equals(source.substring(cursor, cursor+3));
}
private boolean isDateChar(char ch,int start) { private boolean isDateChar(char ch,int start) {
int eot = source.charAt(start+1) == 'T' ? 10 : 20; int eot = source.charAt(start+1) == 'T' ? 10 : 20;
@ -550,5 +565,11 @@ public class FHIRLexer {
public SourceLocation getCommentLocation() { public SourceLocation getCommentLocation() {
return this.commentLocation; return this.commentLocation;
} }
public boolean isMetadataFormat() {
return metadataFormat;
}
public void setMetadataFormat(boolean metadataFormat) {
this.metadataFormat = metadataFormat;
}
} }

View File

@ -6352,5 +6352,8 @@ public class FHIRPathEngine {
this.liquidMode = liquidMode; this.liquidMode = liquidMode;
} }
public ProfileUtilities getProfileUtilities() {
return profileUtilities;
}
} }

View File

@ -619,16 +619,38 @@ public class StructureMapUtilities {
} }
public StructureMap parse(String text, String srcName) throws FHIRException { public StructureMap parse(String text, String srcName) throws FHIRException {
FHIRLexer lexer = new FHIRLexer(text, srcName); FHIRLexer lexer = new FHIRLexer(Utilities.stripBOM(text), srcName, true);
if (lexer.done()) if (lexer.done())
throw lexer.error("Map Input cannot be empty"); throw lexer.error("Map Input cannot be empty");
lexer.token("map");
StructureMap result = new StructureMap(); StructureMap result = new StructureMap();
result.setUrl(lexer.readConstant("url")); if (lexer.hasToken("map")) {
lexer.token("="); lexer.token("map");
result.setName(lexer.readConstant("name")); result.setUrl(lexer.readConstant("url"));
result.setDescription(lexer.getAllComments()); lexer.token("=");
result.setStatus(PublicationStatus.DRAFT); result.setName(lexer.readConstant("name"));
result.setDescription(lexer.getAllComments());
result.setStatus(PublicationStatus.DRAFT);
} else {
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;
default:
lexer.readConstant("nothing");
// nothing
}
}
}
while (lexer.hasToken("conceptmap")) while (lexer.hasToken("conceptmap"))
parseConceptMap(result, lexer); parseConceptMap(result, lexer);
@ -978,11 +1000,11 @@ public class StructureMapUtilities {
// type and cardinality // type and cardinality
lexer.token(":"); lexer.token(":");
source.setType(lexer.takeDottedToken()); source.setType(lexer.takeDottedToken());
if (!lexer.hasToken("as", "first", "last", "not_first", "not_last", "only_one", "default")) { }
source.setMin(lexer.takeInt()); if (Utilities.isInteger(lexer.getCurrent())) {
lexer.token(".."); source.setMin(lexer.takeInt());
source.setMax(lexer.take()); lexer.token("..");
} source.setMax(lexer.take());
} }
if (lexer.hasToken("default")) { if (lexer.hasToken("default")) {
lexer.token("default"); lexer.token("default");

View File

@ -214,9 +214,17 @@ public class VersionUtilities {
} else if (Utilities.charCount(version, '.') == 2) { } else if (Utilities.charCount(version, '.') == 2) {
String[] p = version.split("\\."); String[] p = version.split("\\.");
return p[0]+"."+p[1]; return p[0]+"."+p[1];
} else { } else if (Utilities.existsInList(version, "R2", "R2B", "R3", "R4", "R4B", "R5")) {
return null; switch (version) {
case "R2": return "1.0";
case "R2B": return "1.4";
case "R3": return "3.0";
case "R4": return "4.0";
case "R4B": return "4.3";
case "R5": return "5.0";
}
} }
return null;
} }
public static String getPatch(String version) { public static String getPatch(String version) {

View File

@ -775,7 +775,7 @@ public class I18nConstants {
public static final String ILLEGAL_COMMENT_TYPE = "ILLEGAL_COMMENT_TYPE"; public static final String ILLEGAL_COMMENT_TYPE = "ILLEGAL_COMMENT_TYPE";
public static final String SD_NO_SLICING_ON_ROOT = "SD_NO_SLICING_ON_ROOT"; public static final String SD_NO_SLICING_ON_ROOT = "SD_NO_SLICING_ON_ROOT";
public static final String REFERENCE_REF_QUERY_INVALID = "REFERENCE_REF_QUERY_INVALID"; public static final String REFERENCE_REF_QUERY_INVALID = "REFERENCE_REF_QUERY_INVALID";
public static final String SM_EXTENDS_NOT_SUPPORTED = ""; public static final String SM_RULEGROUP_NOT_FOUND = "SM_RULEGROUP_NOT_FOUND";
public static final String SM_NAME_INVALID = "SM_NAME_INVALID"; public static final String SM_NAME_INVALID = "SM_NAME_INVALID";
public static final String SM_GROUP_INPUT_DUPLICATE = "SM_GROUP_INPUT_DUPLICATE"; public static final String SM_GROUP_INPUT_DUPLICATE = "SM_GROUP_INPUT_DUPLICATE";
public static final String SM_GROUP_INPUT_MODE_INVALID = "SM_GROUP_INPUT_MODE_INVALID"; public static final String SM_GROUP_INPUT_MODE_INVALID = "SM_GROUP_INPUT_MODE_INVALID";
@ -803,6 +803,7 @@ public class I18nConstants {
public static final String SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE = "SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE"; public static final String SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE = "SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE";
public static final String SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE = "SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE"; public static final String SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE = "SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE";
public static final String SM_TARGET_TRANSFORM_EXPRESSION_ERROR = "SM_TARGET_TRANSFORM_EXPRESSION_ERROR"; public static final String SM_TARGET_TRANSFORM_EXPRESSION_ERROR = "SM_TARGET_TRANSFORM_EXPRESSION_ERROR";
public static final String SM_IMPORT_NOT_FOUND = "SM_IMPORT_NOT_FOUND";
} }

View File

@ -825,13 +825,13 @@ EXT_VER_URL_REVERSION = The extension URL must not contain a version. The extens
ILLEGAL_COMMENT_TYPE = The fhir_comments property must be an array of strings ILLEGAL_COMMENT_TYPE = The fhir_comments property must be an array of strings
SD_NO_SLICING_ON_ROOT = Slicing is not allowed at the root of a profile SD_NO_SLICING_ON_ROOT = Slicing is not allowed at the root of a profile
REFERENCE_REF_QUERY_INVALID = The query part of the conditional reference is not a valid query string ({0}) REFERENCE_REF_QUERY_INVALID = The query part of the conditional reference is not a valid query string ({0})
SM_EXTENDS_NOT_SUPPORTED = Group.extends is not supported SM_RULEGROUP_NOT_FOUND = The group {0} could not be resolved
SM_NAME_INVALID = The name {0} is not valid SM_NAME_INVALID = The name {0} is not valid
SM_GROUP_INPUT_DUPLICATE = The name {0} is already used SM_GROUP_INPUT_DUPLICATE = The name {0} is already used
SM_GROUP_INPUT_MODE_INVALID = The group parameter {0} mode {1} isn't valid SM_GROUP_INPUT_MODE_INVALID = The group parameter {0} mode {1} isn''t valid
SM_GROUP_INPUT_NO_TYPE = The group parameter {0} has no type, so the paths cannot be validated SM_GROUP_INPUT_NO_TYPE = The group parameter {0} has no type, so the paths cannot be validated
SM_GROUP_INPUT_TYPE_NOT_DECLARED = The type {0} was not declared and is unknown SM_GROUP_INPUT_TYPE_NOT_DECLARED = The type {0} was not declared and is unknown
SM_GROUP_INPUT_MODE_MISMATCH = The type {0} has mode {1} which doesn't match the structure definition {2} SM_GROUP_INPUT_MODE_MISMATCH = The type {0} has mode {1} which doesn''t match the structure definition {2}
SM_GROUP_INPUT_TYPE_UNKNOWN = The type {0} which maps to the canonical URL {1} is not known, so the paths cannot be validated SM_GROUP_INPUT_TYPE_UNKNOWN = The type {0} which maps to the canonical URL {1} is not known, so the paths cannot be validated
SM_SOURCE_CONTEXT_UNKNOWN = The source context {0} is not known at this point SM_SOURCE_CONTEXT_UNKNOWN = The source context {0} is not known at this point
SM_SOURCE_PATH_INVALID = The source path {0}.{1} refers to the path {2} which is unknown SM_SOURCE_PATH_INVALID = The source path {0}.{1} refers to the path {2} which is unknown
@ -842,17 +842,17 @@ SM_TARGET_CONTEXT_UNKNOWN = The target context {0} is not known at this point
SM_TARGET_PATH_INVALID = The target path {0}.{1} refers to the path {2} which is unknown SM_TARGET_PATH_INVALID = The target path {0}.{1} refers to the path {2} which is unknown
SM_NO_LIST_MODE_NEEDED = A list mode should not be provided since this is a rule that can only be executed once SM_NO_LIST_MODE_NEEDED = A list mode should not be provided since this is a rule that can only be executed once
SM_NO_LIST_RULE_ID_NEEDED = A list ruleId should not be provided since this is a rule that can only be executed once SM_NO_LIST_RULE_ID_NEEDED = A list ruleId should not be provided since this is a rule that can only be executed once
SM_LIST_RULE_ID_ONLY_WHEN_SHARE = A ruleId should only be provided when the rule mode is 'share' SM_LIST_RULE_ID_ONLY_WHEN_SHARE = A ruleId should only be provided when the rule mode is ''share''
SM_RULE_SOURCE_UNASSIGNED = The source statement doesn't assign a variable to the source - check that this is what is intended SM_RULE_SOURCE_UNASSIGNED = The source statement doesn''t assign a variable to the source - check that this is what is intended
SM_TARGET_PATH_MULTIPLE_MATCHES = The target path {0}.{1} refers to the path {2} which is could be a reference to multiple elements ({3}). No further checking can be performed SM_TARGET_PATH_MULTIPLE_MATCHES = The target path {0}.{1} refers to the path {2} which is could be a reference to multiple elements ({3}). No further checking can be performed
SM_SOURCE_TYPE_INVALID = The type {0} is not valid in this context {1}. The possible types are [{2}] SM_SOURCE_TYPE_INVALID = The type {0} is not valid in this source context {1}. The possible types are [{2}]
SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE = Transform {0} takes {1}-{2} parameter(s) but {3} were found SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE = Transform {0} takes {1}-{2} parameter(s) but {3} were found
SM_TARGET_TRANSFORM_PARAM_COUNT_SINGLE = Transform {0} takes {1} parameter(s) but {2} were found SM_TARGET_TRANSFORM_PARAM_COUNT_SINGLE = Transform {0} takes {1} parameter(s) but {2} were found
SM_TARGET_TRANSFORM_NOT_CHECKED = Transform {0} not checked yet SM_TARGET_TRANSFORM_NOT_CHECKED = Transform {0} not checked yet
SM_TARGET_NO_TRANSFORM_NO_CHECKED = When there is no transform, parameters can't be provided SM_TARGET_NO_TRANSFORM_NO_CHECKED = When there is no transform, parameters can''t be provided
SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE = The value of the type parameter could not be processed SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE = The value of the type parameter could not be processed
SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE = The parameter at index {0} could not be processed (type = {1}) SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE = The parameter at index {0} could not be processed (type = {1})
SM_TARGET_TRANSFORM_EXPRESSION_ERROR = The FHIRPath expression passed as the evaluate parameter is invalid: {0} SM_TARGET_TRANSFORM_EXPRESSION_ERROR = The FHIRPath expression passed as the evaluate parameter is invalid: {0}
SM_IMPORT_NOT_FOUND = No maps were found to match {0} - validation may be wrong

View File

@ -20,6 +20,7 @@ import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.IWorkerContext.IContextResourceLoader;
import org.hl7.fhir.r5.context.SimpleWorkerContext; import org.hl7.fhir.r5.context.SimpleWorkerContext;
import org.hl7.fhir.r5.elementmodel.Manager; import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.JsonParser;
@ -112,7 +113,9 @@ public class IgLoader {
if (!srcPackage.contains("#")) { if (!srcPackage.contains("#")) {
System.out.print("#" + npm.version()); System.out.print("#" + npm.version());
} }
int count = getContext().loadFromPackage(npm, ValidatorUtils.loaderForVersion(npm.fhirVersion())); IContextResourceLoader loader = ValidatorUtils.loaderForVersion(npm.fhirVersion());
loader.setPatchUrls(VersionUtilities.isCorePackage(npm.id()));
int count = getContext().loadFromPackage(npm, loader);
System.out.println(" - " + count + " resources (" + getContext().clock().milestone() + ")"); System.out.println(" - " + count + " resources (" + getContext().clock().milestone() + ")");
} else { } else {
System.out.print(" Load " + srcPackage); System.out.print(" Load " + srcPackage);
@ -183,8 +186,10 @@ public class IgLoader {
res.cntType = Manager.FhirFormat.TURTLE; res.cntType = Manager.FhirFormat.TURTLE;
else if (t.getKey().endsWith(".shc")) else if (t.getKey().endsWith(".shc"))
res.cntType = Manager.FhirFormat.SHC; res.cntType = Manager.FhirFormat.SHC;
else if (t.getKey().endsWith(".txt") || t.getKey().endsWith(".map")) else if (t.getKey().endsWith(".txt"))
res.cntType = Manager.FhirFormat.TEXT; res.cntType = Manager.FhirFormat.TEXT;
else if (t.getKey().endsWith(".fml") || t.getKey().endsWith(".map"))
res.cntType = Manager.FhirFormat.FML;
else else
throw new FHIRException("Todo: Determining resource type is not yet done"); throw new FHIRException("Todo: Determining resource type is not yet done");
} }
@ -762,6 +767,7 @@ public class IgLoader {
if (isDebug() || ((e.getMessage() != null && e.getMessage().contains("cannot be cast")))) { if (isDebug() || ((e.getMessage() != null && e.getMessage().contains("cannot be cast")))) {
e.printStackTrace(); e.printStackTrace();
} }
e.printStackTrace();
} }
return r; return r;
} }
@ -774,7 +780,7 @@ public class IgLoader {
res = new org.hl7.fhir.dstu3.formats.XmlParser().parse(new ByteArrayInputStream(content)); res = new org.hl7.fhir.dstu3.formats.XmlParser().parse(new ByteArrayInputStream(content));
else if (fn.endsWith(".json") && !fn.endsWith("template.json")) else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
res = new org.hl7.fhir.dstu3.formats.JsonParser().parse(new ByteArrayInputStream(content)); res = new org.hl7.fhir.dstu3.formats.JsonParser().parse(new ByteArrayInputStream(content));
else if (fn.endsWith(".txt") || fn.endsWith(".map")) else if (fn.endsWith(".txt") || fn.endsWith(".map") || fn.endsWith(".fml"))
res = new org.hl7.fhir.dstu3.utils.StructureMapUtilities(null).parse(new String(content)); res = new org.hl7.fhir.dstu3.utils.StructureMapUtilities(null).parse(new String(content));
else else
throw new FHIRException("Unsupported format for " + fn); throw new FHIRException("Unsupported format for " + fn);
@ -785,7 +791,7 @@ public class IgLoader {
res = new org.hl7.fhir.r4.formats.XmlParser().parse(new ByteArrayInputStream(content)); res = new org.hl7.fhir.r4.formats.XmlParser().parse(new ByteArrayInputStream(content));
else if (fn.endsWith(".json") && !fn.endsWith("template.json")) else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
res = new org.hl7.fhir.r4.formats.JsonParser().parse(new ByteArrayInputStream(content)); res = new org.hl7.fhir.r4.formats.JsonParser().parse(new ByteArrayInputStream(content));
else if (fn.endsWith(".txt") || fn.endsWith(".map")) else if (fn.endsWith(".txt") || fn.endsWith(".map") || fn.endsWith(".fml"))
res = new org.hl7.fhir.r4.utils.StructureMapUtilities(null).parse(new String(content), fn); res = new org.hl7.fhir.r4.utils.StructureMapUtilities(null).parse(new String(content), fn);
else else
throw new FHIRException("Unsupported format for " + fn); throw new FHIRException("Unsupported format for " + fn);
@ -796,7 +802,7 @@ public class IgLoader {
res = new org.hl7.fhir.r4b.formats.XmlParser().parse(new ByteArrayInputStream(content)); res = new org.hl7.fhir.r4b.formats.XmlParser().parse(new ByteArrayInputStream(content));
else if (fn.endsWith(".json") && !fn.endsWith("template.json")) else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
res = new org.hl7.fhir.r4b.formats.JsonParser().parse(new ByteArrayInputStream(content)); res = new org.hl7.fhir.r4b.formats.JsonParser().parse(new ByteArrayInputStream(content));
else if (fn.endsWith(".txt") || fn.endsWith(".map")) else if (fn.endsWith(".txt") || fn.endsWith(".map") || fn.endsWith(".fml"))
res = new org.hl7.fhir.r4b.utils.structuremap.StructureMapUtilities(null).parse(new String(content), fn); res = new org.hl7.fhir.r4b.utils.structuremap.StructureMapUtilities(null).parse(new String(content), fn);
else else
throw new FHIRException("Unsupported format for " + fn); throw new FHIRException("Unsupported format for " + fn);
@ -819,15 +825,15 @@ public class IgLoader {
else else
throw new FHIRException("Unsupported format for " + fn); throw new FHIRException("Unsupported format for " + fn);
r = VersionConvertorFactory_10_50.convertResource(res, new org.hl7.fhir.convertors.misc.IGR2ConvertorAdvisor5()); r = VersionConvertorFactory_10_50.convertResource(res, new org.hl7.fhir.convertors.misc.IGR2ConvertorAdvisor5());
} else if (fhirVersion.startsWith("5.0")) { } else if (fhirVersion.startsWith("5.0") || "current".equals(fhirVersion)) {
if (fn.endsWith(".xml") && !fn.endsWith("template.xml")) if (fn.endsWith(".xml") && !fn.endsWith("template.xml"))
r = new XmlParser().parse(new ByteArrayInputStream(content)); r = new XmlParser().parse(new ByteArrayInputStream(content));
else if (fn.endsWith(".json") && !fn.endsWith("template.json")) else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
r = new JsonParser().parse(new ByteArrayInputStream(content)); r = new JsonParser().parse(new ByteArrayInputStream(content));
else if (fn.endsWith(".txt")) else if (fn.endsWith(".txt"))
r = new StructureMapUtilities(getContext(), null, null).parse(TextFile.bytesToString(content), fn); r = new StructureMapUtilities(getContext(), null, null).parse(TextFile.bytesToString(content), fn);
else if (fn.endsWith(".map")) else if (fn.endsWith(".map") || fn.endsWith(".fml"))
r = new StructureMapUtilities(null).parse(new String(content), fn); r = new StructureMapUtilities(context).parse(new String(content), fn);
else else
throw new FHIRException("Unsupported format for " + fn); throw new FHIRException("Unsupported format for " + fn);
} else } else

View File

@ -219,6 +219,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
@Getter @Setter private Coding jurisdiction; @Getter @Setter private Coding jurisdiction;
private ContextUtilities cu = null;
/** /**
* Creating a validation engine is an expensive operation - takes seconds. * Creating a validation engine is an expensive operation - takes seconds.
* Once you have a validation engine created, you can quickly clone it to * Once you have a validation engine created, you can quickly clone it to
@ -842,7 +844,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
new org.hl7.fhir.dstu3.formats.XmlParser().setOutputStyle(org.hl7.fhir.dstu3.formats.IParser.OutputStyle.PRETTY).compose(s, res); new org.hl7.fhir.dstu3.formats.XmlParser().setOutputStyle(org.hl7.fhir.dstu3.formats.IParser.OutputStyle.PRETTY).compose(s, res);
else if (fn.endsWith(".json") && !fn.endsWith("template.json")) else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
new org.hl7.fhir.dstu3.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu3.formats.IParser.OutputStyle.PRETTY).compose(s, res); new org.hl7.fhir.dstu3.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu3.formats.IParser.OutputStyle.PRETTY).compose(s, res);
else if (fn.endsWith(".txt") || fn.endsWith(".map")) else if (fn.endsWith(".txt") || fn.endsWith(".map") || fn.endsWith(".fml"))
TextFile.stringToStream(org.hl7.fhir.dstu3.utils.StructureMapUtilities.render((org.hl7.fhir.dstu3.model.StructureMap) res), s, false); TextFile.stringToStream(org.hl7.fhir.dstu3.utils.StructureMapUtilities.render((org.hl7.fhir.dstu3.model.StructureMap) res), s, false);
else else
throw new FHIRException("Unsupported format for " + fn); throw new FHIRException("Unsupported format for " + fn);
@ -852,7 +854,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
new org.hl7.fhir.r4.formats.XmlParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).compose(s, res); new org.hl7.fhir.r4.formats.XmlParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).compose(s, res);
else if (fn.endsWith(".json") && !fn.endsWith("template.json")) else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
new org.hl7.fhir.r4.formats.JsonParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).compose(s, res); new org.hl7.fhir.r4.formats.JsonParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).compose(s, res);
else if (fn.endsWith(".txt") || fn.endsWith(".map")) else if (fn.endsWith(".txt") || fn.endsWith(".map") || fn.endsWith(".fml"))
TextFile.stringToStream(org.hl7.fhir.r4.utils.StructureMapUtilities.render((org.hl7.fhir.r4.model.StructureMap) res), s, false); TextFile.stringToStream(org.hl7.fhir.r4.utils.StructureMapUtilities.render((org.hl7.fhir.r4.model.StructureMap) res), s, false);
else else
throw new FHIRException("Unsupported format for " + fn); throw new FHIRException("Unsupported format for " + fn);
@ -877,7 +879,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
new XmlParser().setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(s, r); new XmlParser().setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(s, r);
else if (fn.endsWith(".json") && !fn.endsWith("template.json")) else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
new JsonParser().setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(s, r); new JsonParser().setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(s, r);
else if (fn.endsWith(".txt") || fn.endsWith(".map")) else if (fn.endsWith(".txt") || fn.endsWith(".map") || fn.endsWith(".fml"))
TextFile.stringToStream(StructureMapUtilities.render((org.hl7.fhir.r5.model.StructureMap) r), s, false); TextFile.stringToStream(StructureMapUtilities.render((org.hl7.fhir.r5.model.StructureMap) r), s, false);
else else
throw new FHIRException("Unsupported format for " + fn); throw new FHIRException("Unsupported format for " + fn);
@ -1061,6 +1063,16 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
resolvedUrls.put(type+"|"+url, false); resolvedUrls.put(type+"|"+url, false);
return false; // todo... how to access settings from here? return false; // todo... how to access settings from here?
} }
if (url.contains("*") && !url.contains("?")) {
if (cu == null) {
cu = new ContextUtilities(context);
}
List<StructureMap> maps = cu.listMaps(url);
if (!maps.isEmpty()) {
return true;
}
}
if (fetcher != null) { if (fetcher != null) {
try { try {
boolean ok = fetcher.resolveURL(validator, appContext, path, url, type); boolean ok = fetcher.resolveURL(validator, appContext, path, url, type);

View File

@ -105,6 +105,8 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IV
} }
if (base.equals("http://terminology.hl7.org")) { if (base.equals("http://terminology.hl7.org")) {
pid = "hl7.terminology"; pid = "hl7.terminology";
} else if (base.equals("http://hl7.org/fhir")) {
return false;
} else if (url.startsWith("http://hl7.org/fhir")) { } else if (url.startsWith("http://hl7.org/fhir")) {
pid = pcm.getPackageId(base); pid = pcm.getPackageId(base);
} else { } else {

View File

@ -14,6 +14,7 @@ import org.hl7.fhir.validation.cli.model.HtmlInMarkdownCheck;
public class Params { public class Params {
public static final String VERSION = "-version"; public static final String VERSION = "-version";
public static final String ALT_VERSION = "-alt-version";
public static final String OUTPUT = "-output"; public static final String OUTPUT = "-output";
public static final String OUTPUT_SUFFIX = "-outputSuffix"; public static final String OUTPUT_SUFFIX = "-outputSuffix";
@ -307,9 +308,29 @@ public class Params {
if (version == null) { if (version == null) {
cliContext.addIg(s); cliContext.addIg(s);
} else { } else {
cliContext.setSv(version); String v = getParam(args, VERSION);
if (v != null && !v.equals(version)) {
throw new Error("Parameters are inconsistent: specified version is "+v+" but -ig parameter "+s+" implies a different version");
} else if (cliContext.getSv() != null && !version.equals(cliContext.getSv())) {
throw new Error("Parameters are inconsistent: multiple -ig parameters implying differetion versions ("+cliContext.getSv()+","+version+")");
} else {
cliContext.setSv(version);
}
} }
} }
} else if (args[i].equals(ALT_VERSION)) {
if (i + 1 == args.length)
throw new Error("Specified " + args[i] + " without indicating version");
else {
String s = args[++i];
String v = VersionUtilities.getMajMin(s);
if (v == null) {
throw new Error("Unsupported FHIR Version "+s);
}
String pid = VersionUtilities.packageForVersion(v);
pid = pid + "#"+VersionUtilities.getCurrentPackageVersion(v);
cliContext.addIg(pid);
}
} else if (args[i].equals(MAP)) { } else if (args[i].equals(MAP)) {
if (cliContext.getMap() == null) { if (cliContext.getMap() == null) {
if (i + 1 == args.length) if (i + 1 == args.length)
@ -336,6 +357,7 @@ public class Params {
cliContext.addSource(args[i]); cliContext.addSource(args[i]);
} }
} }
return cliContext; return cliContext;
} }

View File

@ -18,6 +18,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
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;
@ -34,6 +35,9 @@ import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.StructureMap; import org.hl7.fhir.r5.model.StructureMap;
import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupComponent;
import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupInputComponent;
import org.hl7.fhir.r5.model.StructureMap.StructureMapInputMode;
import org.hl7.fhir.r5.model.TypeDetails; import org.hl7.fhir.r5.model.TypeDetails;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine;
@ -226,6 +230,8 @@ public class StructureMapValidator extends BaseValidator {
private FHIRPathEngine fpe; private FHIRPathEngine fpe;
private ProfileUtilities profileUtilities; private ProfileUtilities profileUtilities;
private ContextUtilities cu;
private List<StructureMap> imports = new ArrayList<>();
public StructureMapValidator(IWorkerContext context, TimeTracker timeTracker, FHIRPathEngine fpe, XVerExtensionManager xverManager, ProfileUtilities profileUtilities, Coding jurisdiction) { public StructureMapValidator(IWorkerContext context, TimeTracker timeTracker, FHIRPathEngine fpe, XVerExtensionManager xverManager, ProfileUtilities profileUtilities, Coding jurisdiction) {
super(context, xverManager); super(context, xverManager);
@ -234,13 +240,21 @@ public class StructureMapValidator extends BaseValidator {
this.timeTracker = timeTracker; this.timeTracker = timeTracker;
this.jurisdiction = jurisdiction; this.jurisdiction = jurisdiction;
this.profileUtilities = profileUtilities; this.profileUtilities = profileUtilities;
this.cu = new ContextUtilities(context);
} }
public boolean validateStructureMap(List<ValidationMessage> errors, Element src, NodeStack stack) { public boolean validateStructureMap(List<ValidationMessage> errors, Element src, NodeStack stack) {
boolean ok = true; boolean ok = true;
List<Element> groups = src.getChildrenByName("group"); List<Element> imports = src.getChildrenByName("import");
int cc = 0; int cc = 0;
for (Element import_ : imports) {
ok = validateImport(errors, src, import_, stack.push(import_, cc, null, null)) && ok;
cc++;
}
List<Element> groups = src.getChildrenByName("group");
cc = 0;
for (Element group : groups) { for (Element group : groups) {
ok = validateGroup(errors, src, group, stack.push(group, cc, null, null)) && ok; ok = validateGroup(errors, src, group, stack.push(group, cc, null, null)) && ok;
cc++; cc++;
@ -248,14 +262,34 @@ public class StructureMapValidator extends BaseValidator {
return ok; return ok;
} }
private boolean validateImport(List<ValidationMessage> errors, Element src, Element import_, NodeStack stack) {
String url = import_.primitiveValue();
boolean ok = false;
StructureMap map = context.fetchResource(StructureMap.class, url);
if (map != null) {
imports.add(map);
ok = true;
} else if (url.contains("*")) {
List<StructureMap> maps = cu.listMaps(url);
ok = !maps.isEmpty();
imports.addAll(maps);
}
warning(errors, "2023-03-01", IssueType.INVALID, import_.line(), import_.col(), stack.getLiteralPath(), ok, I18nConstants.SM_IMPORT_NOT_FOUND, url);
return true;
}
private boolean validateGroup(List<ValidationMessage> errors, Element src, Element group, NodeStack stack) { private boolean validateGroup(List<ValidationMessage> errors, Element src, Element group, NodeStack stack) {
String name = group.getChildValue("name"); String name = group.getChildValue("name");
boolean ok = rule(errors, "2023-02-17", IssueType.INVALID, group.line(), group.col(), stack.getLiteralPath(), idIsValid(name), I18nConstants.SM_NAME_INVALID, name); boolean ok = rule(errors, "2023-03-01", IssueType.INVALID, group.line(), group.col(), stack.getLiteralPath(), idIsValid(name), I18nConstants.SM_NAME_INVALID, name);
Element extend = src.getNamedChild("extends"); Element extend = group.getNamedChild("extends");
if (extend != null) { if (extend != null) {
rule(errors, "2023-02-17", IssueType.NOTSUPPORTED, extend.line(), extend.col(), stack.push(extend, -1, null, null).getLiteralPath(), false, I18nConstants.SM_EXTENDS_NOT_SUPPORTED, extend.primitiveValue()); StructureMapGroupComponent grp = resolveGroup(extend.primitiveValue(), src);
ok = false; if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, extend.line(), extend.col(), stack.push(extend, -1, null, null).getLiteralPath(), grp != null, I18nConstants.SM_RULEGROUP_NOT_FOUND, extend.primitiveValue())) {
// check inputs
} else {
ok = false;
}
} }
VariableSet variables = new VariableSet(); VariableSet variables = new VariableSet();
@ -280,24 +314,62 @@ public class StructureMapValidator extends BaseValidator {
return ok; return ok;
} }
private StructureMapGroupComponent resolveGroup(String grpName, Element src) {
if (grpName == null) {
return null;
}
List<Element> groups = src.getChildrenByName("group");
for (Element group : groups) {
String name = group.getChildValue("name");
if (grpName.equals(name)) {
return makeGroupComponent(group);
}
}
for (StructureMap map : imports) {
for (StructureMapGroupComponent grp : map.getGroup()) {
if (grpName.equals(grp.getName())) {
return grp;
}
}
}
return null;
}
private StructureMapGroupComponent makeGroupComponent(Element group) {
StructureMapGroupComponent grp = new StructureMapGroupComponent();
grp.setName(group.getChildValue("name"));
List<Element> inputs = group.getChildrenByName("input");
for (Element input : inputs) {
StructureMapGroupInputComponent inp = grp.addInput();
inp.setName(input.getChildValue("name"));
inp.setType(input.getChildValue("type"));
try {
inp.setMode(StructureMapInputMode.fromCode(input.getChildValue("mode")));
} catch (Exception e) {
// nothing; will be an error elsewhere
}
}
return grp;
}
private boolean validateInput(List<ValidationMessage> errors, Element src, Element group, Element input, NodeStack stack, List<Element> structures, VariableSet variables) { private boolean validateInput(List<ValidationMessage> errors, Element src, Element group, Element input, NodeStack stack, List<Element> structures, VariableSet variables) {
boolean ok = false; boolean ok = false;
String name = input.getChildValue("name"); String name = input.getChildValue("name");
String type = input.getChildValue("type"); String type = input.getChildValue("type");
String mode = input.getChildValue("mode"); String mode = input.getChildValue("mode");
if (rule(errors, "2023-02-17", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), idIsValid(name), I18nConstants.SM_NAME_INVALID, name) && // the name {0} is not valid) if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), idIsValid(name), I18nConstants.SM_NAME_INVALID, name) && // the name {0} is not valid)
rule(errors, "2023-02-17", IssueType.DUPLICATE, input.line(), input.col(), stack.getLiteralPath(), !variables.hasVariable(name), I18nConstants.SM_GROUP_INPUT_DUPLICATE, name)) { // the name {0} is not valid) rule(errors, "2023-03-01", IssueType.DUPLICATE, input.line(), input.col(), stack.getLiteralPath(), !variables.hasVariable(name), I18nConstants.SM_GROUP_INPUT_DUPLICATE, name)) { // the name {0} is not valid)
VariableDefn v = variables.add(name, mode); VariableDefn v = variables.add(name, mode);
if (rule(errors, "2023-02-17", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), Utilities.existsInList(mode, "source", "target"), I18nConstants.SM_GROUP_INPUT_MODE_INVALID, name, mode) && // the group parameter {0} mode {1} isn't valid if (rule(errors, "2023-03-01", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), Utilities.existsInList(mode, "source", "target"), I18nConstants.SM_GROUP_INPUT_MODE_INVALID, name, mode) && // the group parameter {0} mode {1} isn't valid
warning(errors, "2023-02-17", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), type != null, I18nConstants.SM_GROUP_INPUT_NO_TYPE, name)) { // the group parameter {0} has no type, so the paths cannot be validated warning(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), type != null, I18nConstants.SM_GROUP_INPUT_NO_TYPE, name)) { // the group parameter {0} has no type, so the paths cannot be validated
Element structure = findStructure(structures, type); Element structure = findStructure(structures, type);
if (rule(errors, "2023-02-17", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), structure != null, I18nConstants.SM_GROUP_INPUT_TYPE_NOT_DECLARED, type)) { // the type {0} was not declared and is unknown if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), structure != null, I18nConstants.SM_GROUP_INPUT_TYPE_NOT_DECLARED, type)) { // the type {0} was not declared and is unknown
String url = structure.getChildValue("url"); String url = structure.getChildValue("url");
String smode = structure.getChildValue("mode"); String smode = structure.getChildValue("mode");
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
if (rule(errors, "2023-02-17", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), mode.equals(smode), I18nConstants.SM_GROUP_INPUT_MODE_MISMATCH, type, mode, smode) && // the type {0} has mode {1} which doesn't match the structure definition {2} if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, input.line(), input.col(), stack.getLiteralPath(), mode.equals(smode), I18nConstants.SM_GROUP_INPUT_MODE_MISMATCH, type, mode, smode) && // the type {0} has mode {1} which doesn't match the structure definition {2}
rule(errors, "2023-02-17", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), sd != null, I18nConstants.SM_GROUP_INPUT_TYPE_UNKNOWN, type, url)) { // the type {0} which maps to the canonical URL {1} is not known, so the paths cannot be validated rule(errors, "2023-03-01", IssueType.INVALID, input.line(), input.col(), stack.getLiteralPath(), sd != null, I18nConstants.SM_GROUP_INPUT_TYPE_UNKNOWN, type, url)) { // the type {0} which maps to the canonical URL {1} is not known, so the paths cannot be validated
v.setType(1, sd, sd.getSnapshot().getElementFirstRep(), null); v.setType(1, sd, sd.getSnapshot().getElementFirstRep(), null);
ok = true; ok = true;
} }
@ -323,7 +395,7 @@ public class StructureMapValidator extends BaseValidator {
private boolean validateRule(List<ValidationMessage> errors, Element src, Element group, Element rule, NodeStack stack, VariableSet variables) { private boolean validateRule(List<ValidationMessage> errors, Element src, Element group, Element rule, NodeStack stack, VariableSet variables) {
String name = rule.getChildValue("name"); String name = rule.getChildValue("name");
boolean ok = rule(errors, "2023-02-17", IssueType.INVALID, rule.line(), rule.col(), stack.getLiteralPath(), idIsValid(name), I18nConstants.SM_NAME_INVALID, name); boolean ok = rule(errors, "2023-03-01", IssueType.INVALID, rule.line(), rule.col(), stack.getLiteralPath(), idIsValid(name), I18nConstants.SM_NAME_INVALID, name);
RuleInformation ruleInfo = new RuleInformation(); RuleInformation ruleInfo = new RuleInformation();
// process the sources // process the sources
@ -350,14 +422,19 @@ public class StructureMapValidator extends BaseValidator {
cc++; cc++;
} }
// todo: check dependents // todo: check dependents
List<Element> dependents = rule.getChildrenByName("dependent");
cc = 0;
for (Element dependent : dependents) {
ok = validateDependent(errors, src, group, dependent, stack.push(dependent, cc, null, null), lvars) && ok;
cc++;
}
return ok; return ok;
} }
private boolean validateRuleSource(List<ValidationMessage> errors, Element src, Element group, Element rule, Element source, NodeStack stack, VariableSet variables, RuleInformation ruleInfo) { private boolean validateRuleSource(List<ValidationMessage> errors, Element src, Element group, Element rule, Element source, NodeStack stack, VariableSet variables, RuleInformation ruleInfo) {
String context = source.getChildValue("context"); String context = source.getChildValue("context");
boolean ok = rule(errors, "2023-02-17", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), idIsValid(context), I18nConstants.SM_NAME_INVALID, context) && boolean ok = rule(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), idIsValid(context), I18nConstants.SM_NAME_INVALID, context) &&
rule(errors, "2023-02-17", IssueType.UNKNOWN, source.line(), source.col(), stack.getLiteralPath(), variables.hasVariable(context, SOURCE), I18nConstants.SM_SOURCE_CONTEXT_UNKNOWN, context); rule(errors, "2023-03-01", IssueType.UNKNOWN, source.line(), source.col(), stack.getLiteralPath(), variables.hasVariable(context, SOURCE), I18nConstants.SM_SOURCE_CONTEXT_UNKNOWN, context);
if (ok) { if (ok) {
VariableDefn v = variables.getVariable(context, SOURCE); VariableDefn v = variables.getVariable(context, SOURCE);
if (v.hasTypeInfo()) { // if it doesn't, that's already an issue elsewhere if (v.hasTypeInfo()) { // if it doesn't, that's already an issue elsewhere
@ -369,8 +446,8 @@ public class StructureMapValidator extends BaseValidator {
String path = v.getEd().getPath()+"."+element; String path = v.getEd().getPath()+"."+element;
String variable = source.getChildValue("variable"); String variable = source.getChildValue("variable");
VariableDefn vn = null; VariableDefn vn = null;
if (hint(errors, "2023-02-17", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), variable != null, I18nConstants.SM_RULE_SOURCE_UNASSIGNED)) { if (hint(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), variable != null, I18nConstants.SM_RULE_SOURCE_UNASSIGNED)) {
if (rule(errors, "2023-02-17", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), idIsValid(variable), I18nConstants.SM_NAME_INVALID, variable)) { if (rule(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), idIsValid(variable), I18nConstants.SM_NAME_INVALID, variable)) {
vn = variables.add(variable, v.getMode()); // may overwrite vn = variables.add(variable, v.getMode()); // may overwrite
} else { } else {
ok = false; ok = false;
@ -378,20 +455,20 @@ public class StructureMapValidator extends BaseValidator {
} }
List<ElementDefinitionSource> els = getElementDefinitions(v.getSd(), v.getEd(), v.getType(), element); List<ElementDefinitionSource> els = getElementDefinitions(v.getSd(), v.getEd(), v.getType(), element);
if (rule(errors, "2023-02-17", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), !els.isEmpty(), I18nConstants.SM_SOURCE_PATH_INVALID, context, element, path)) { if (rule(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), !els.isEmpty(), I18nConstants.SM_SOURCE_PATH_INVALID, context, element, path)) {
if (warning(errors, "2023-02-17", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), els.size() == 1, I18nConstants.SM_TARGET_PATH_MULTIPLE_MATCHES, context, element, v.getEd().getPath()+"."+element, render(els))) { if (warning(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), els.size() == 1, I18nConstants.SM_TARGET_PATH_MULTIPLE_MATCHES, context, element, v.getEd().getPath()+"."+element, render(els))) {
ElementDefinitionSource el = els.get(0); ElementDefinitionSource el = els.get(0);
String type = source.getChildValue("type"); String type = source.getChildValue("type");
if (type != null) { if (type != null) {
ok = rule(errors, "2023-02-17", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), hasType(el.getEd(), type), I18nConstants.SM_SOURCE_TYPE_INVALID, type, path, el.getEd().typeSummary()) && ok; ok = rule(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), hasType(el.getEd(), type), I18nConstants.SM_SOURCE_TYPE_INVALID, type, path, el.getEd().typeSummary()) && ok;
} }
String min = source.getChildValue("min"); String min = source.getChildValue("min");
hint(errors, "2023-02-17", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), min == null || isMoreOrEqual(min, v.getEd().getMin()), I18nConstants.SM_RULE_SOURCE_MIN_REDUNDANT, min, v.getEd().getMin()); hint(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), min == null || isMoreOrEqual(min, v.getEd().getMin()), I18nConstants.SM_RULE_SOURCE_MIN_REDUNDANT, min, v.getEd().getMin());
int existingMax = multiplyCardinality(v.getMax(), el.getEd().getMax()); int existingMax = multiplyCardinality(v.getMax(), el.getEd().getMax());
String max = source.getChildValue("max"); String max = source.getChildValue("max");
int iMax = readMax(max, existingMax); int iMax = readMax(max, existingMax);
warning(errors, "2023-02-17", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), iMax <= existingMax, I18nConstants.SM_RULE_SOURCE_MAX_REDUNDANT, max, v.getMax()); warning(errors, "2023-03-01", IssueType.INVALID, source.line(), source.col(), stack.getLiteralPath(), iMax <= existingMax, I18nConstants.SM_RULE_SOURCE_MAX_REDUNDANT, max, v.getMax());
ruleInfo.seeCardinality(iMax); ruleInfo.seeCardinality(iMax);
@ -440,22 +517,22 @@ public class StructureMapValidator extends BaseValidator {
if (context == null) { if (context == null) {
return true; return true;
} }
boolean ok = rule(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), idIsValid(context), I18nConstants.SM_NAME_INVALID, context) && boolean ok = rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), idIsValid(context), I18nConstants.SM_NAME_INVALID, context) &&
rule(errors, "2023-02-17", IssueType.UNKNOWN, target.line(), target.col(), stack.getLiteralPath(), variables.hasVariable(context, TARGET), I18nConstants.SM_TARGET_CONTEXT_UNKNOWN, context); rule(errors, "2023-03-01", IssueType.UNKNOWN, target.line(), target.col(), stack.getLiteralPath(), variables.hasVariable(context, TARGET), I18nConstants.SM_TARGET_CONTEXT_UNKNOWN, context);
if (ok) { if (ok) {
VariableDefn v = variables.getVariable(context, TARGET); VariableDefn v = variables.getVariable(context, TARGET);
if (v.hasTypeInfo()) { if (v.hasTypeInfo()) {
String listMode = target.getChildValue("listMode"); String listMode = target.getChildValue("listMode");
String listRuleId = target.getChildValue("listRuleId"); String listRuleId = target.getChildValue("listRuleId");
warning(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), listRuleId == null || "share".equals(listMode), I18nConstants.SM_LIST_RULE_ID_ONLY_WHEN_SHARE); warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), listRuleId == null || "share".equals(listMode), I18nConstants.SM_LIST_RULE_ID_ONLY_WHEN_SHARE);
if (!ruleInfo.isList()) { if (!ruleInfo.isList()) {
warning(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), listMode == null, I18nConstants.SM_NO_LIST_MODE_NEEDED); warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), listMode == null, I18nConstants.SM_NO_LIST_MODE_NEEDED);
warning(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), listRuleId == null, I18nConstants.SM_NO_LIST_RULE_ID_NEEDED); warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), listRuleId == null, I18nConstants.SM_NO_LIST_RULE_ID_NEEDED);
} }
VariableDefn vn = null; VariableDefn vn = null;
String variable = target.getChildValue("variable"); String variable = target.getChildValue("variable");
if (variable != null) { if (variable != null) {
if (rule(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), idIsValid(variable), I18nConstants.SM_NAME_INVALID, variable)) { if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), idIsValid(variable), I18nConstants.SM_NAME_INVALID, variable)) {
vn = variables.add(variable, v.getMode()); // may overwrite vn = variables.add(variable, v.getMode()); // may overwrite
} else { } else {
ok = false; ok = false;
@ -465,21 +542,21 @@ public class StructureMapValidator extends BaseValidator {
String element = target.getChildValue("element"); String element = target.getChildValue("element");
if (element != null) { if (element != null) {
List<ElementDefinitionSource> els = getElementDefinitions(v.getSd(), v.getEd(), v.getType(), element); List<ElementDefinitionSource> els = getElementDefinitions(v.getSd(), v.getEd(), v.getType(), element);
if (rule(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), !els.isEmpty(), I18nConstants.SM_TARGET_PATH_INVALID, context, element, v.getEd().getPath()+"."+element)) { if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), !els.isEmpty(), I18nConstants.SM_TARGET_PATH_INVALID, context, element, v.getEd().getPath()+"."+element)) {
if (warning(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), els.size() == 1, I18nConstants.SM_TARGET_PATH_MULTIPLE_MATCHES, context, element, v.getEd().getPath()+"."+element, render(els))) { if (warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), els.size() == 1, I18nConstants.SM_TARGET_PATH_MULTIPLE_MATCHES, context, element, v.getEd().getPath()+"."+element, render(els))) {
ElementDefinitionSource el = els.get(0); ElementDefinitionSource el = els.get(0);
String type = null; // maybe inferred / derived from transform in the future String type = null; // maybe inferred / derived from transform in the future
String transform = target.getChildValue("transform"); String transform = target.getChildValue("transform");
List<Element> params = target.getChildren("parameter"); List<Element> params = target.getChildren("parameter");
if (transform == null) { if (transform == null) {
rule(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 0, I18nConstants.SM_TARGET_NO_TRANSFORM_NO_CHECKED, transform); rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 0, I18nConstants.SM_TARGET_NO_TRANSFORM_NO_CHECKED, transform);
} else { } else {
switch (transform) { switch (transform) {
case "create": case "create":
if (rule(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() < 2, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE, "create", "0", "1", params.size())) { if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() < 2, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE, "create", "0", "1", params.size())) {
if (params.size() == 1) { if (params.size() == 1) {
type = params.get(0).primitiveValue(); type = params.get(0).primitiveValue();
warning(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(),type != null, I18nConstants.SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE); warning(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(),type != null, I18nConstants.SM_TARGET_TRANSFORM_TYPE_UNPROCESSIBLE);
} else { } else {
// maybe can guess? maybe not ... type = // maybe can guess? maybe not ... type =
} }
@ -488,23 +565,23 @@ public class StructureMapValidator extends BaseValidator {
} }
break; break;
case "reference": case "reference":
if (rule(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 1, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE, "reference", "0", "1", params.size())) { if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 1, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_RANGE, "reference", "0", "1", params.size())) {
type = "string"; type = "string";
} else { } else {
ok = false; ok = false;
} }
break; break;
case "evaluate": case "evaluate":
if (rule(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 1, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_SINGLE, "evaluate", "1", params.size())) { if (rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), params.size() == 1, I18nConstants.SM_TARGET_TRANSFORM_PARAM_COUNT_SINGLE, "evaluate", "1", params.size())) {
String exp = params.get(0).primitiveValue(); String exp = params.get(0).primitiveValue();
if (rule(errors, "2023-02-17", IssueType.INVALID, params.get(0).line(), params.get(0).col(), stack.getLiteralPath(), exp != null, I18nConstants.SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE, "0", params.size())) { if (rule(errors, "2023-03-01", IssueType.INVALID, params.get(0).line(), params.get(0).col(), stack.getLiteralPath(), exp != null, I18nConstants.SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE, "0", params.size())) {
try { try {
TypeDetails td = fpe.check(null, v.getSd().getType(), v.getEd().getPath(), fpe.parse(exp)); TypeDetails td = fpe.check(null, v.getSd().getType(), v.getEd().getPath(), fpe.parse(exp));
if (td.getTypes().size() == 1) { if (td.getTypes().size() == 1) {
type = td.getType(); type = td.getType();
} }
} catch (Exception e) { } catch (Exception e) {
rule(errors, "2023-02-17", IssueType.INVALID, params.get(0).line(), params.get(0).col(), stack.getLiteralPath(), false, I18nConstants.SM_TARGET_TRANSFORM_EXPRESSION_ERROR, e.getMessage()); rule(errors, "2023-03-01", IssueType.INVALID, params.get(0).line(), params.get(0).col(), stack.getLiteralPath(), false, I18nConstants.SM_TARGET_TRANSFORM_EXPRESSION_ERROR, e.getMessage());
} }
} else { } else {
ok = false; ok = false;
@ -514,7 +591,7 @@ public class StructureMapValidator extends BaseValidator {
} }
break; break;
default: default:
rule(errors, "2023-02-17", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), false, I18nConstants.SM_TARGET_TRANSFORM_NOT_CHECKED, transform); rule(errors, "2023-03-01", IssueType.INVALID, target.line(), target.col(), stack.getLiteralPath(), false, I18nConstants.SM_TARGET_TRANSFORM_NOT_CHECKED, transform);
ok = false; ok = false;
} }
} }
@ -590,4 +667,17 @@ public class StructureMapValidator extends BaseValidator {
return true; // no issue in this case return true; // no issue in this case
} }
private boolean validateDependent(List<ValidationMessage> errors, Element src, Element group, Element dependent, NodeStack stack, VariableSet lvars) {
boolean ok = true;
String name = dependent.getChildValue("name");
StructureMapGroupComponent grp = resolveGroup(name, src);
if (rule(errors, "2023-03-01", IssueType.NOTSUPPORTED, dependent.line(), dependent.col(), stack.push(dependent, -1, null, null).getLiteralPath(), grp != null, I18nConstants.SM_RULEGROUP_NOT_FOUND, name)) {
// check inputs
} else {
ok = false;
}
return ok;
}
} }