Merge branch 'hapifhir:master' into master
This commit is contained in:
commit
4c4156d539
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -469,10 +469,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
tlog("$expand on " + txCache.summary(vs));
|
tlog("$expand on " + txCache.summary(vs));
|
||||||
try {
|
try {
|
||||||
ValueSet result = txClient.expandValueset(vs, p, params);
|
ValueSet result = txClient.expandValueset(vs, p, params);
|
||||||
|
if (result != null) {
|
||||||
if (!result.hasUrl())
|
if (!result.hasUrl())
|
||||||
result.setUrl(vs.getUrl());
|
result.setUrl(vs.getUrl());
|
||||||
if (!result.hasUrl())
|
if (!result.hasUrl())
|
||||||
throw new Error("no url in expand value set 2");
|
throw new Error("no url in expand value set 2");
|
||||||
|
}
|
||||||
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
|
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
|
res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
|
||||||
|
|
|
@ -276,9 +276,6 @@ public class FhirRequestBuilder {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
String s = ResourceUtilities.getErrorDescription(error);
|
String s = ResourceUtilities.getErrorDescription(error);
|
||||||
System.out.println(s);
|
System.out.println(s);
|
||||||
if (s.startsWith("Unable to find value set")) {
|
|
||||||
System.out.println("!");
|
|
||||||
}
|
|
||||||
throw new EFhirClientException("Error from "+source+": " + ResourceUtilities.getErrorDescription(error), error);
|
throw new EFhirClientException("Error from "+source+": " + ResourceUtilities.getErrorDescription(error), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -805,12 +805,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
tlog("$expand on " + txCache.summary(vs));
|
tlog("$expand on " + txCache.summary(vs));
|
||||||
try {
|
try {
|
||||||
ValueSet result = txClient.expandValueset(vs, p, params);
|
ValueSet result = txClient.expandValueset(vs, p, params);
|
||||||
|
if (result != null) {
|
||||||
if (!result.hasUrl()) {
|
if (!result.hasUrl()) {
|
||||||
result.setUrl(vs.getUrl());
|
result.setUrl(vs.getUrl());
|
||||||
}
|
}
|
||||||
if (!result.hasUrl()) {
|
if (!result.hasUrl()) {
|
||||||
throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET_2));
|
throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET_2));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
|
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
|
res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -952,12 +952,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
txLog("$expand on "+txCache.summary(vs));
|
txLog("$expand on "+txCache.summary(vs));
|
||||||
try {
|
try {
|
||||||
ValueSet result = tcc.getClient().expandValueset(vs, p, params);
|
ValueSet result = tcc.getClient().expandValueset(vs, p, params);
|
||||||
|
if (result != null) {
|
||||||
if (!result.hasUrl()) {
|
if (!result.hasUrl()) {
|
||||||
result.setUrl(vs.getUrl());
|
result.setUrl(vs.getUrl());
|
||||||
}
|
}
|
||||||
if (!result.hasUrl()) {
|
if (!result.hasUrl()) {
|
||||||
throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET_2));
|
throw new Error(formatMessage(I18nConstants.NO_URL_IN_EXPAND_VALUE_SET_2));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
|
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (res != null && !res.isFromServer()) {
|
if (res != null && !res.isFromServer()) {
|
||||||
|
@ -1375,7 +1377,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
if (valueSet != null) {
|
if (valueSet != null) {
|
||||||
pIn.addParameter().setName("valueSet").setResource(valueSet);
|
pIn.addParameter().setName("valueSet").setResource(valueSet);
|
||||||
}
|
}
|
||||||
pIn.addParameter().setName("profile").setResource(expParameters);
|
|
||||||
|
pIn.addParameters(expParameters);
|
||||||
return pIn;
|
return pIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1388,7 +1391,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
if (vsUrl != null) {
|
if (vsUrl != null) {
|
||||||
pIn.addParameter().setName("url").setValue(new CanonicalType(vsUrl));
|
pIn.addParameter().setName("url").setValue(new CanonicalType(vsUrl));
|
||||||
}
|
}
|
||||||
pIn.addParameter().setName("profile").setResource(expParameters);
|
pIn.addParameters(expParameters);
|
||||||
return pIn;
|
return pIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1548,7 +1551,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
if (expParameters == null) {
|
if (expParameters == null) {
|
||||||
throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED));
|
throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED));
|
||||||
}
|
}
|
||||||
pin.addParameter().setName("profile").setResource(expParameters);
|
pin.addParameters(expParameters);
|
||||||
|
|
||||||
if (options.isDisplayWarningMode()) {
|
if (options.isDisplayWarningMode()) {
|
||||||
pin.addParameter("mode","lenient-display-validation");
|
pin.addParameter("mode","lenient-display-validation");
|
||||||
|
|
|
@ -130,7 +130,6 @@ public abstract class ParserBase {
|
||||||
|
|
||||||
public abstract void compose(Element e, OutputStream destination, OutputStyle style, String base) throws FHIRException, IOException;
|
public abstract void compose(Element e, OutputStream destination, OutputStyle style, String base) throws FHIRException, IOException;
|
||||||
|
|
||||||
//FIXME: i18n should be done here
|
|
||||||
public void logError(List<ValidationMessage> errors, String ruleDate, int line, int col, String path, IssueType type, String message, IssueSeverity level) throws FHIRFormatError {
|
public void logError(List<ValidationMessage> errors, String ruleDate, int line, int col, String path, IssueType type, String message, IssueSeverity level) throws FHIRFormatError {
|
||||||
if (errors != null) {
|
if (errors != null) {
|
||||||
if (policy == ValidationPolicy.EVERYTHING) {
|
if (policy == ValidationPolicy.EVERYTHING) {
|
||||||
|
|
|
@ -730,4 +730,8 @@ public class ExpressionNode {
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isNullSet() {
|
||||||
|
return kind == Kind.Constant && constant == null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1856,6 +1856,18 @@ public String toString() {
|
||||||
getParameter().removeIf(p -> name.equals(p.getName()));
|
getParameter().removeIf(p -> name.equals(p.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addParameters(Parameters expParameters) {
|
||||||
|
addParameters(expParameters.getParameter());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addParameters(List<ParametersParameterComponent> parameters) {
|
||||||
|
for (ParametersParameterComponent p : parameters) {
|
||||||
|
if (!hasParameter(p.getName())) {
|
||||||
|
addParameter(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// end addition
|
// end addition
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -542,7 +542,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
|
||||||
} else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrlLinkable(pcv.getValue().primitiveValue())) {
|
} else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrlLinkable(pcv.getValue().primitiveValue())) {
|
||||||
td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue());
|
td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue());
|
||||||
} else if ("parent".equals(pcv.getCode())) {
|
} else if ("parent".equals(pcv.getCode())) {
|
||||||
td.ah("#"+cs.getId()+"-"+pcv.getValue().primitiveValue()).addText(pcv.getValue().primitiveValue());
|
td.ah("#"+cs.getId()+"-"+Utilities.nmtokenize(pcv.getValue().primitiveValue())).addText(pcv.getValue().primitiveValue());
|
||||||
} else {
|
} else {
|
||||||
td.addText(pcv.getValue().primitiveValue());
|
td.addText(pcv.getValue().primitiveValue());
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,9 +161,17 @@ public class DataRenderer extends Renderer implements CodeResolver {
|
||||||
if (p == null)
|
if (p == null)
|
||||||
p = getContext().getWorker().fetchResource(StructureDefinition.class, link);
|
p = getContext().getWorker().fetchResource(StructureDefinition.class, link);
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
|
if ("Extension".equals(p.getType())) {
|
||||||
|
path = null;
|
||||||
|
} else if (p.hasSnapshot()) {
|
||||||
|
path = p.getSnapshot().getElementFirstRep().getPath();
|
||||||
|
} else if (Utilities.isAbsoluteUrl(path)) {
|
||||||
|
path = null;
|
||||||
|
}
|
||||||
url = p.getWebPath();
|
url = p.getWebPath();
|
||||||
if (url == null)
|
if (url == null) {
|
||||||
url = p.getUserString("filename");
|
url = p.getUserString("filename");
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
throw new DefinitionException("Unable to resolve markdown link "+link);
|
throw new DefinitionException("Unable to resolve markdown link "+link);
|
||||||
|
|
||||||
|
|
|
@ -100,15 +100,22 @@ public class SearchParameterRenderer extends TerminologyRenderer {
|
||||||
}
|
}
|
||||||
tr = tbl.tr();
|
tr = tbl.tr();
|
||||||
tr.td().tx("Multiples");
|
tr.td().tx("Multiples");
|
||||||
if (spd.getMultipleAnd() && spd.getMultipleOr()) {
|
XhtmlNode ul = tr.td().ul();
|
||||||
tr.td().tx("The parameter can repeat (and) and can have repeating values (or)");
|
if (!spd.hasMultipleAnd()) {
|
||||||
} else if (spd.getMultipleOr()) {
|
ul.li().tx("multipleAnd: It's up to the server whether the parameter may repeat in order to specify multiple values that must all be true");
|
||||||
tr.td().tx("The parameter can repeat (and) but each repeat can only have one value");
|
|
||||||
} else if (spd.getMultipleAnd()) {
|
} else if (spd.getMultipleAnd()) {
|
||||||
tr.td().tx("The parameter cannot repeat (and) but the single parameter can have multiple values (or)");
|
ul.li().tx("multipleAnd: The parameter may repeat in order to specify multiple values that must all be true");
|
||||||
} else {
|
} else {
|
||||||
tr.td().tx("The parameter cannot repeat or have multiple values");
|
ul.li().tx("multipleAnd: The parameter may only appear once");
|
||||||
}
|
}
|
||||||
|
if (!spd.hasMultipleOr()) {
|
||||||
|
ul.li().tx("multipleOr: It's up to the server whether the parameter can have multiple values (separated by comma) where at least one must be true");
|
||||||
|
} else if (spd.getMultipleOr()) {
|
||||||
|
ul.li().tx("multipleOr: The parameter may have multiple values (separated by comma) where at least one must be true");
|
||||||
|
} else {
|
||||||
|
ul.li().tx("multipleOr: The parameter may only have one value (no comma separators)");
|
||||||
|
}
|
||||||
|
|
||||||
if (spd.hasComparator()) {
|
if (spd.hasComparator()) {
|
||||||
tr = tbl.tr();
|
tr = tbl.tr();
|
||||||
tr.td().tx("Comparators");
|
tr.td().tx("Comparators");
|
||||||
|
|
|
@ -190,6 +190,7 @@ public class RenderingContext {
|
||||||
private String changeVersion;
|
private String changeVersion;
|
||||||
|
|
||||||
private Map<KnownLinkType, String> links = new HashMap<>();
|
private Map<KnownLinkType, String> links = new HashMap<>();
|
||||||
|
private Map<String, String> namedLinks = new HashMap<>();
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param context - access to all related resources that might be needed
|
* @param context - access to all related resources that might be needed
|
||||||
|
@ -720,5 +721,8 @@ public class RenderingContext {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getNamedLinks() {
|
||||||
|
return namedLinks;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -78,6 +78,7 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||||
import org.hl7.fhir.utilities.xhtml.NodeType;
|
import org.hl7.fhir.utilities.xhtml.NodeType;
|
||||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.fhirpath.FhirPathExecutionException;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
import ca.uhn.fhir.util.ElementUtil;
|
||||||
|
|
||||||
|
@ -511,6 +512,7 @@ public class FHIRPathEngine {
|
||||||
}
|
}
|
||||||
types.addType(sd.getUrl());
|
types.addType(sd.getUrl());
|
||||||
} else {
|
} else {
|
||||||
|
boolean checkTypeName = false;
|
||||||
String ctxt = null;
|
String ctxt = null;
|
||||||
if (t.contains("#")) {
|
if (t.contains("#")) {
|
||||||
ctxt = t.substring(0, t.indexOf('#'));
|
ctxt = t.substring(0, t.indexOf('#'));
|
||||||
|
@ -518,6 +520,7 @@ public class FHIRPathEngine {
|
||||||
} else if (Utilities.isAbsoluteUrl(t)) {
|
} else if (Utilities.isAbsoluteUrl(t)) {
|
||||||
ctxt = t;
|
ctxt = t;
|
||||||
t = ctxt.substring(ctxt.lastIndexOf("/")+1);
|
t = ctxt.substring(ctxt.lastIndexOf("/")+1);
|
||||||
|
checkTypeName = true;
|
||||||
} else {
|
} else {
|
||||||
ctxt = t.substring(0, t.indexOf('.'));
|
ctxt = t.substring(0, t.indexOf('.'));
|
||||||
}
|
}
|
||||||
|
@ -525,7 +528,9 @@ public class FHIRPathEngine {
|
||||||
if (sd == null) {
|
if (sd == null) {
|
||||||
throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, t);
|
throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, t);
|
||||||
}
|
}
|
||||||
ElementDefinitionMatch ed = getElementDefinition(sd, t, true, expr);
|
String tn = checkTypeName ? sd.getSnapshot().getElementFirstRep().getPath() : t;
|
||||||
|
|
||||||
|
ElementDefinitionMatch ed = getElementDefinition(sd, tn, true, expr);
|
||||||
if (ed == null) {
|
if (ed == null) {
|
||||||
throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, t);
|
throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, t);
|
||||||
}
|
}
|
||||||
|
@ -3087,7 +3092,12 @@ public class FHIRPathEngine {
|
||||||
} else if (hostServices == null) {
|
} else if (hostServices == null) {
|
||||||
throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s);
|
throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s);
|
||||||
} else {
|
} else {
|
||||||
return hostServices.resolveConstantType(this, context.appInfo, s, explicitConstant);
|
TypeDetails v = hostServices.resolveConstantType(this, context.appInfo, s, explicitConstant);
|
||||||
|
if (v == null) {
|
||||||
|
throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s);
|
||||||
|
} else {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3141,7 +3151,7 @@ public class FHIRPathEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypeDetails executeType(String type, ExpressionNode exp, boolean atEntry, TypeDetails focus, Set<ElementDefinition> elementDependencies) throws PathEngineException, DefinitionException {
|
private TypeDetails executeType(String type, ExpressionNode exp, boolean atEntry, TypeDetails focus, Set<ElementDefinition> elementDependencies) throws PathEngineException, DefinitionException {
|
||||||
if (atEntry && Character.isUpperCase(exp.getName().charAt(0)) && hashTail(type).equals(exp.getName())) { // special case for start up
|
if (atEntry && Character.isUpperCase(exp.getName().charAt(0)) && (hashTail(type).equals(exp.getName()) || isAncestor(type, exp.getName()) )) { // special case for start up
|
||||||
return new TypeDetails(CollectionStatus.SINGLETON, type);
|
return new TypeDetails(CollectionStatus.SINGLETON, type);
|
||||||
}
|
}
|
||||||
TypeDetails result = new TypeDetails(focus.getCollectionStatus());
|
TypeDetails result = new TypeDetails(focus.getCollectionStatus());
|
||||||
|
@ -3150,6 +3160,21 @@ public class FHIRPathEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isAncestor(String wanted, String stated) {
|
||||||
|
try {
|
||||||
|
StructureDefinition sd = worker.fetchTypeDefinition(wanted);
|
||||||
|
while (sd != null) {
|
||||||
|
if (stated.equals(sd.getTypeName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
sd = worker.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String hashTail(String type) {
|
private String hashTail(String type) {
|
||||||
return type.contains("#") ? "" : type.substring(type.lastIndexOf("/")+1);
|
return type.contains("#") ? "" : type.substring(type.lastIndexOf("/")+1);
|
||||||
}
|
}
|
||||||
|
@ -3646,7 +3671,7 @@ public class FHIRPathEngine {
|
||||||
|
|
||||||
|
|
||||||
private void checkContextString(TypeDetails focus, String name, ExpressionNode expr, boolean sing) throws PathEngineException {
|
private void checkContextString(TypeDetails focus, String name, ExpressionNode expr, boolean sing) throws PathEngineException {
|
||||||
if (!focus.hasNoTypes() && !focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "canonical") && !focus.hasType(worker, "id")) {
|
if (!focus.hasNoTypes() && !focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "url") && !focus.hasType(worker, "canonical") && !focus.hasType(worker, "id")) {
|
||||||
throw makeException(expr, sing ? I18nConstants.FHIRPATH_STRING_SING_ONLY : I18nConstants.FHIRPATH_STRING_ORD_ONLY, name, focus.describe());
|
throw makeException(expr, sing ? I18nConstants.FHIRPATH_STRING_SING_ONLY : I18nConstants.FHIRPATH_STRING_ORD_ONLY, name, focus.describe());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4314,7 +4339,11 @@ public class FHIRPathEngine {
|
||||||
if (x == null) {
|
if (x == null) {
|
||||||
return makeBoolean(false);
|
return makeBoolean(false);
|
||||||
}
|
}
|
||||||
return makeBoolean(checkHtmlNames(x));
|
boolean ok = checkHtmlNames(x, true);
|
||||||
|
if (ok && VersionUtilities.isR6Plus(this.worker.getVersion())) {
|
||||||
|
ok = checkForContent(x);
|
||||||
|
}
|
||||||
|
return makeBoolean(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Base> funcHtmlChecks2(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
private List<Base> funcHtmlChecks2(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||||
|
@ -4383,12 +4412,13 @@ public class FHIRPathEngine {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkHtmlNames(XhtmlNode node) {
|
private boolean checkHtmlNames(XhtmlNode node, boolean block) {
|
||||||
if (node.getNodeType() == NodeType.Comment) {
|
if (node.getNodeType() == NodeType.Comment) {
|
||||||
if (node.getContent().startsWith("DOCTYPE"))
|
if (node.getContent().startsWith("DOCTYPE"))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (node.getNodeType() == NodeType.Element) {
|
if (node.getNodeType() == NodeType.Element) {
|
||||||
|
if (block) {
|
||||||
if (!Utilities.existsInList(node.getName(),
|
if (!Utilities.existsInList(node.getName(),
|
||||||
"p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong",
|
"p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong",
|
||||||
"small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup",
|
"small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup",
|
||||||
|
@ -4396,6 +4426,12 @@ public class FHIRPathEngine {
|
||||||
"code", "samp", "img", "map", "area")) {
|
"code", "samp", "img", "map", "area")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (!Utilities.existsInList(node.getName(),
|
||||||
|
"a", "span", "b", "em", "i", "strong", "small", "big", "small", "q", "var", "abbr", "acronym", "cite", "kbd", "q", "sub", "sup", "code", "samp", "img", "map", "area")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (String an : node.getAttributes().keySet()) {
|
for (String an : node.getAttributes().keySet()) {
|
||||||
boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an,
|
boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an,
|
||||||
"title", "style", "class", "id", "lang", "xml:lang", "dir", "accesskey", "tabindex",
|
"title", "style", "class", "id", "lang", "xml:lang", "dir", "accesskey", "tabindex",
|
||||||
|
@ -4413,7 +4449,7 @@ public class FHIRPathEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (XhtmlNode c : node.getChildNodes()) {
|
for (XhtmlNode c : node.getChildNodes()) {
|
||||||
if (!checkHtmlNames(c)) {
|
if (!checkHtmlNames(c, block && !"p".equals(c))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6087,6 +6123,7 @@ public class FHIRPathEngine {
|
||||||
|
|
||||||
|
|
||||||
public ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName, ExpressionNode expr) throws PathEngineException {
|
public ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName, ExpressionNode expr) throws PathEngineException {
|
||||||
|
|
||||||
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
|
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
|
||||||
if (ed.getPath().equals(path)) {
|
if (ed.getPath().equals(path)) {
|
||||||
if (ed.hasContentReference()) {
|
if (ed.hasContentReference()) {
|
||||||
|
|
|
@ -13,6 +13,12 @@ public class Cell {
|
||||||
this.column = column;
|
this.column = column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cell(Column column, Value value) {
|
||||||
|
super();
|
||||||
|
this.column = column;
|
||||||
|
this.values.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
public Column getColumn() {
|
public Column getColumn() {
|
||||||
return column;
|
return column;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ public class Column {
|
||||||
private String type;
|
private String type;
|
||||||
private ColumnKind kind;
|
private ColumnKind kind;
|
||||||
private boolean isColl;
|
private boolean isColl;
|
||||||
|
private boolean duplicateReported;
|
||||||
|
|
||||||
protected Column() {
|
protected Column() {
|
||||||
super();
|
super();
|
||||||
|
@ -58,5 +59,41 @@ public class Column {
|
||||||
this.isColl = isColl;
|
this.isColl = isColl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String diff(Column other) {
|
||||||
|
if (!name.equals(other.name)) {
|
||||||
|
return "Names differ: '"+name+"' vs '"+other.name+"'";
|
||||||
|
}
|
||||||
|
if (kind != ColumnKind.Null && other.kind != ColumnKind.Null) {
|
||||||
|
if (length != other.length) {
|
||||||
|
return "Lengths differ: '"+length+"' vs '"+other.length+"'";
|
||||||
|
}
|
||||||
|
if (kind != other.kind) {
|
||||||
|
return "Kinds differ: '"+kind+"' vs '"+other.kind+"'";
|
||||||
|
}
|
||||||
|
if (isColl != other.isColl) {
|
||||||
|
return "Collection status differs: '"+isColl+"' vs '"+other.isColl+"'";
|
||||||
|
}
|
||||||
|
} else if (kind == ColumnKind.Null) {
|
||||||
|
kind = other.kind;
|
||||||
|
length = other.length;
|
||||||
|
isColl = other.isColl;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDuplicateReported() {
|
||||||
|
return duplicateReported;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDuplicateReported(boolean duplicateReported) {
|
||||||
|
this.duplicateReported = duplicateReported;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Column [name=" + name + ", length=" + length + ", type=" + type + ", kind=" + kind + ", isColl=" + isColl
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package org.hl7.fhir.r5.utils.sql;
|
package org.hl7.fhir.r5.utils.sql;
|
||||||
|
|
||||||
public enum ColumnKind {
|
public enum ColumnKind {
|
||||||
String, DateTime, Integer, Decimal, Binary, Time, Boolean, Complex
|
String, DateTime, Integer, Decimal, Binary, Time, Boolean, Complex, Null
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,5 @@ import org.hl7.fhir.r5.model.Base;
|
||||||
public interface Provider {
|
public interface Provider {
|
||||||
List<Base> fetch(String resourceType);
|
List<Base> fetch(String resourceType);
|
||||||
|
|
||||||
Base resolveReference(String ref, String resourceType);
|
Base resolveReference(Base rootResource, String ref, String specifiedResourceType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||||
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
|
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
|
||||||
import org.hl7.fhir.r5.utils.FHIRPathUtilityClasses.FunctionDetails;
|
import org.hl7.fhir.r5.utils.FHIRPathUtilityClasses.FunctionDetails;
|
||||||
import org.hl7.fhir.utilities.json.model.JsonObject;
|
import org.hl7.fhir.utilities.json.model.JsonObject;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
|
|
||||||
|
|
||||||
public class Runner implements IEvaluationContext {
|
public class Runner implements IEvaluationContext {
|
||||||
|
@ -33,9 +34,8 @@ public class Runner implements IEvaluationContext {
|
||||||
private List<String> prohibitedNames = new ArrayList<String>();
|
private List<String> prohibitedNames = new ArrayList<String>();
|
||||||
private FHIRPathEngine fpe;
|
private FHIRPathEngine fpe;
|
||||||
|
|
||||||
private List<Column> columns = new ArrayList<>();
|
|
||||||
|
|
||||||
private String resourceName;
|
private String resourceName;
|
||||||
|
private List<ValidationMessage> issues;
|
||||||
|
|
||||||
|
|
||||||
public IWorkerContext getContext() {
|
public IWorkerContext getContext() {
|
||||||
|
@ -86,15 +86,15 @@ public class Runner implements IEvaluationContext {
|
||||||
}
|
}
|
||||||
Validator validator = new Validator(context, fpe, prohibitedNames, storage.supportsArrays(), storage.supportsComplexTypes(), storage.needsName());
|
Validator validator = new Validator(context, fpe, prohibitedNames, storage.supportsArrays(), storage.supportsComplexTypes(), storage.needsName());
|
||||||
validator.checkViewDefinition(path, viewDefinition);
|
validator.checkViewDefinition(path, viewDefinition);
|
||||||
|
issues = validator.getIssues();
|
||||||
validator.dump();
|
validator.dump();
|
||||||
validator.check();
|
validator.check();
|
||||||
resourceName = validator.getResourceName();
|
resourceName = validator.getResourceName();
|
||||||
columns = validator.getColumns();
|
|
||||||
evaluate(viewDefinition);
|
evaluate(viewDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void evaluate(JsonObject vd) {
|
private void evaluate(JsonObject vd) {
|
||||||
Store store = storage.createStore(vd.asString("name"), columns);
|
Store store = storage.createStore(vd.asString("name"), (List<Column>) vd.getUserData("columns"));
|
||||||
|
|
||||||
List<Base> data = provider.fetch(resourceName);
|
List<Base> data = provider.fetch(resourceName);
|
||||||
|
|
||||||
|
@ -130,7 +130,20 @@ public class Runner implements IEvaluationContext {
|
||||||
if (select.has("forEach")) {
|
if (select.has("forEach")) {
|
||||||
focus.addAll(executeForEach(select, b));
|
focus.addAll(executeForEach(select, b));
|
||||||
} else if (select.has("forEachOrNull")) {
|
} else if (select.has("forEachOrNull")) {
|
||||||
|
|
||||||
focus.addAll(executeForEachOrNull(select, b));
|
focus.addAll(executeForEachOrNull(select, b));
|
||||||
|
if (focus.isEmpty()) {
|
||||||
|
List<Column> columns = (List<Column>) select.getUserData("columns");
|
||||||
|
for (List<Cell> row : rows) {
|
||||||
|
for (Column c : columns) {
|
||||||
|
Cell cell = cell(row, c.getName());
|
||||||
|
if (cell == null) {
|
||||||
|
row.add(new Cell(c, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
focus.add(b);
|
focus.add(b);
|
||||||
}
|
}
|
||||||
|
@ -149,19 +162,30 @@ public class Runner implements IEvaluationContext {
|
||||||
executeColumn(column, f, rowsToAdd);
|
executeColumn(column, f, rowsToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (JsonObject sub : select.getJsonObjects("unionAll")) {
|
|
||||||
executeSelect(sub, f, rowsToAdd);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (JsonObject sub : select.getJsonObjects("select")) {
|
for (JsonObject sub : select.getJsonObjects("select")) {
|
||||||
executeSelect(sub, f, rowsToAdd);
|
executeSelect(sub, f, rowsToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executeUnionAll(select.getJsonObjects("unionAll"), f, rowsToAdd);
|
||||||
|
|
||||||
rows.addAll(rowsToAdd);
|
rows.addAll(rowsToAdd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Base> executeUnion(JsonObject focus, Base b, List<List<Cell>> rows) {
|
private void executeUnionAll(List<JsonObject> unionList, Base b, List<List<Cell>> rows) {
|
||||||
throw new FHIRException("union is not supported");
|
if (unionList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<List<Cell>> sourceRows = new ArrayList<>();
|
||||||
|
sourceRows.addAll(rows);
|
||||||
|
rows.clear();
|
||||||
|
|
||||||
|
for (JsonObject union : unionList) {
|
||||||
|
List<List<Cell>> tempRows = new ArrayList<>();
|
||||||
|
tempRows.addAll(sourceRows);
|
||||||
|
executeSelect(union, b, tempRows);
|
||||||
|
rows.addAll(tempRows);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<List<Cell>> cloneRows(List<List<Cell>> rows) {
|
private List<List<Cell>> cloneRows(List<List<Cell>> rows) {
|
||||||
|
@ -191,9 +215,6 @@ public class Runner implements IEvaluationContext {
|
||||||
ExpressionNode n = (ExpressionNode) focus.getUserData("forEachOrNull");
|
ExpressionNode n = (ExpressionNode) focus.getUserData("forEachOrNull");
|
||||||
List<Base> result = new ArrayList<>();
|
List<Base> result = new ArrayList<>();
|
||||||
result.addAll(fpe.evaluate(b, n));
|
result.addAll(fpe.evaluate(b, n));
|
||||||
if (result.size() == 0) {
|
|
||||||
result.add(null);
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,11 +224,14 @@ public class Runner implements IEvaluationContext {
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
bl2.addAll(fpe.evaluate(b, n));
|
bl2.addAll(fpe.evaluate(b, n));
|
||||||
}
|
}
|
||||||
String name = column.getUserString("name");
|
Column col = (Column) column.getUserData("column");
|
||||||
|
if (col == null) {
|
||||||
|
System.out.println("Error");
|
||||||
|
} else {
|
||||||
for (List<Cell> row : rows) {
|
for (List<Cell> row : rows) {
|
||||||
Cell c = cell(row, name);
|
Cell c = cell(row, col.getName());
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
c = new Cell(column(name));
|
c = new Cell(col);
|
||||||
row.add(c);
|
row.add(c);
|
||||||
}
|
}
|
||||||
if (!bl2.isEmpty()) {
|
if (!bl2.isEmpty()) {
|
||||||
|
@ -224,6 +248,7 @@ public class Runner implements IEvaluationContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private Value genValue(Column column, Base b) {
|
private Value genValue(Column column, Base b) {
|
||||||
|
@ -299,7 +324,7 @@ public class Runner implements IEvaluationContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Column column(String columnName) {
|
private Column column(String columnName, List<Column> columns) {
|
||||||
for (Column t : columns) {
|
for (Column t : columns) {
|
||||||
if (t.getName().equalsIgnoreCase(columnName)) {
|
if (t.getName().equalsIgnoreCase(columnName)) {
|
||||||
return t;
|
return t;
|
||||||
|
@ -353,7 +378,7 @@ public class Runner implements IEvaluationContext {
|
||||||
public List<Base> executeFunction(FHIRPathEngine engine, Object appContext, List<Base> focus, String functionName, List<List<Base>> parameters) {
|
public List<Base> executeFunction(FHIRPathEngine engine, Object appContext, List<Base> focus, String functionName, List<List<Base>> parameters) {
|
||||||
switch (functionName) {
|
switch (functionName) {
|
||||||
case "getResourceKey" : return executeResourceKey(focus);
|
case "getResourceKey" : return executeResourceKey(focus);
|
||||||
case "getReferenceKey" : return executeReferenceKey(focus, parameters);
|
case "getReferenceKey" : return executeReferenceKey(null, focus, parameters);
|
||||||
default: throw new Error("Not known: "+functionName);
|
default: throw new Error("Not known: "+functionName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -375,7 +400,7 @@ public class Runner implements IEvaluationContext {
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Base> executeReferenceKey(List<Base> focus, List<List<Base>> parameters) {
|
private List<Base> executeReferenceKey(Base rootResource, List<Base> focus, List<List<Base>> parameters) {
|
||||||
String rt = null;
|
String rt = null;
|
||||||
if (parameters.size() > 0) {
|
if (parameters.size() > 0) {
|
||||||
rt = parameters.get(0).get(0).primitiveValue();
|
rt = parameters.get(0).get(0).primitiveValue();
|
||||||
|
@ -395,7 +420,7 @@ public class Runner implements IEvaluationContext {
|
||||||
throw new FHIRException("Unable to generate a reference key based on a "+res.fhirType());
|
throw new FHIRException("Unable to generate a reference key based on a "+res.fhirType());
|
||||||
}
|
}
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
Base target = provider.resolveReference(ref, rt);
|
Base target = provider.resolveReference(rootResource, ref, rt);
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
if (!res.hasUserData("Storage.key")) {
|
if (!res.hasUserData("Storage.key")) {
|
||||||
String key = storage.getKeyForTargetResource(target);
|
String key = storage.getKeyForTargetResource(target);
|
||||||
|
@ -437,6 +462,9 @@ public class Runner implements IEvaluationContext {
|
||||||
public boolean paramIsType(String name, int index) {
|
public boolean paramIsType(String name, int index) {
|
||||||
return "getReferenceKey".equals(name);
|
return "getReferenceKey".equals(name);
|
||||||
}
|
}
|
||||||
|
public List<ValidationMessage> getIssues() {
|
||||||
|
return issues;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,9 @@ public class StorageJson implements Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonElement makeJsonNode(Value value) {
|
private JsonElement makeJsonNode(Value value) {
|
||||||
if (value.getValueInt() != null) {
|
if (value == null) {
|
||||||
|
return new JsonNull();
|
||||||
|
} else if (value.getValueInt() != null) {
|
||||||
return new JsonNumber(value.getValueInt().intValue());
|
return new JsonNumber(value.getValueInt().intValue());
|
||||||
}
|
}
|
||||||
if (value.getValueBoolean() != null) {
|
if (value.getValueBoolean() != null) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.hl7.fhir.r5.utils.sql;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLType;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
@ -27,7 +28,7 @@ public class StorageSqlite3 implements Storage {
|
||||||
private Connection conn;
|
private Connection conn;
|
||||||
private int nextKey = 0;
|
private int nextKey = 0;
|
||||||
|
|
||||||
protected StorageSqlite3(Connection conn) {
|
public StorageSqlite3(Connection conn) {
|
||||||
super();
|
super();
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,7 @@ public class StorageSqlite3 implements Storage {
|
||||||
CommaSeparatedStringBuilder fields = new CommaSeparatedStringBuilder(", ");
|
CommaSeparatedStringBuilder fields = new CommaSeparatedStringBuilder(", ");
|
||||||
CommaSeparatedStringBuilder values = new CommaSeparatedStringBuilder(", ");
|
CommaSeparatedStringBuilder values = new CommaSeparatedStringBuilder(", ");
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append("Create Table "+name+" { ");
|
b.append("Create Table "+name+" ( ");
|
||||||
b.append("ViewRowKey integer NOT NULL");
|
b.append("ViewRowKey integer NOT NULL");
|
||||||
for (Column column : columns) {
|
for (Column column : columns) {
|
||||||
b.append(", "+column.getName()+" "+sqliteType(column.getKind())+" NULL"); // index columns are always nullable
|
b.append(", "+column.getName()+" "+sqliteType(column.getKind())+" NULL"); // index columns are always nullable
|
||||||
|
@ -48,7 +49,7 @@ public class StorageSqlite3 implements Storage {
|
||||||
b.append(", PRIMARY KEY (ViewRowKey))\r\n");
|
b.append(", PRIMARY KEY (ViewRowKey))\r\n");
|
||||||
conn.createStatement().execute(b.toString());
|
conn.createStatement().execute(b.toString());
|
||||||
|
|
||||||
String isql = "Insert into "+name+" ("+fields.toString()+") values ("+values.toString()+")";
|
String isql = "Insert into "+name+" (ViewRowKey, "+fields.toString()+") values (?, "+values.toString()+")";
|
||||||
PreparedStatement psql = conn.prepareStatement(isql);
|
PreparedStatement psql = conn.prepareStatement(isql);
|
||||||
return new SQLiteStore(name, psql);
|
return new SQLiteStore(name, psql);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -79,6 +80,8 @@ public class StorageSqlite3 implements Storage {
|
||||||
for (int i = 0; i < cells.size(); i++) {
|
for (int i = 0; i < cells.size(); i++) {
|
||||||
Cell c = cells.get(i);
|
Cell c = cells.get(i);
|
||||||
switch (c.getColumn().getKind()) {
|
switch (c.getColumn().getKind()) {
|
||||||
|
case Null:
|
||||||
|
p.setNull(i+2, java.sql.Types.NVARCHAR);
|
||||||
case Binary:
|
case Binary:
|
||||||
p.setBytes(i+2, c.getValues().size() == 0 ? null : c.getValues().get(0).getValueBinary());
|
p.setBytes(i+2, c.getValues().size() == 0 ? null : c.getValues().get(0).getValueBinary());
|
||||||
break;
|
break;
|
||||||
|
@ -103,6 +106,7 @@ public class StorageSqlite3 implements Storage {
|
||||||
case Complex: throw new FHIRException("SQLite runner does not handle complexes");
|
case Complex: throw new FHIRException("SQLite runner does not handle complexes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
p.execute();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new FHIRException(e);
|
throw new FHIRException(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.hl7.fhir.utilities.json.model.JsonBoolean;
|
||||||
import org.hl7.fhir.utilities.json.model.JsonElement;
|
import org.hl7.fhir.utilities.json.model.JsonElement;
|
||||||
import org.hl7.fhir.utilities.json.model.JsonNumber;
|
import org.hl7.fhir.utilities.json.model.JsonNumber;
|
||||||
import org.hl7.fhir.utilities.json.model.JsonObject;
|
import org.hl7.fhir.utilities.json.model.JsonObject;
|
||||||
|
import org.hl7.fhir.utilities.json.model.JsonProperty;
|
||||||
import org.hl7.fhir.utilities.json.model.JsonString;
|
import org.hl7.fhir.utilities.json.model.JsonString;
|
||||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||||
|
@ -35,7 +36,6 @@ public class Validator {
|
||||||
private Boolean needsName;
|
private Boolean needsName;
|
||||||
|
|
||||||
private String resourceName;
|
private String resourceName;
|
||||||
private List<Column> columns = new ArrayList<Column>();
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public Validator(IWorkerContext context, FHIRPathEngine fpe, List<String> prohibitedNames, Boolean arrays, Boolean complexTypes, Boolean needsName) {
|
public Validator(IWorkerContext context, FHIRPathEngine fpe, List<String> prohibitedNames, Boolean arrays, Boolean complexTypes, Boolean needsName) {
|
||||||
|
@ -52,11 +52,10 @@ public class Validator {
|
||||||
return resourceName;
|
return resourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Column> getColumns() {
|
|
||||||
return columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkViewDefinition(String path, JsonObject viewDefinition) {
|
public void checkViewDefinition(String path, JsonObject viewDefinition) {
|
||||||
|
checkProperties(viewDefinition, path, "url", "identifier", "name", "version", "title", "status", "experimental", "date", "publisher", "contact", "description", "useContext", "copyright", "resource", "constant", "select", "where");
|
||||||
|
|
||||||
JsonElement nameJ = viewDefinition.get("name");
|
JsonElement nameJ = viewDefinition.get("name");
|
||||||
if (nameJ == null) {
|
if (nameJ == null) {
|
||||||
if (needsName == null) {
|
if (needsName == null) {
|
||||||
|
@ -76,9 +75,12 @@ public class Validator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Column> columns = new ArrayList<>();
|
||||||
|
viewDefinition.setUserData("columns", columns);
|
||||||
|
|
||||||
JsonElement resourceNameJ = viewDefinition.get("resource");
|
JsonElement resourceNameJ = viewDefinition.get("resource");
|
||||||
if (resourceNameJ == null) {
|
if (resourceNameJ == null) {
|
||||||
error(path, viewDefinition, "No resource provided", IssueType.REQUIRED);
|
error(path, viewDefinition, "No resource specified", IssueType.REQUIRED);
|
||||||
} else if (!(resourceNameJ instanceof JsonString)) {
|
} else if (!(resourceNameJ instanceof JsonString)) {
|
||||||
error(path, viewDefinition, "resource must be a string", IssueType.INVALID);
|
error(path, viewDefinition, "resource must be a string", IssueType.INVALID);
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,17 +104,10 @@ public class Validator {
|
||||||
}
|
}
|
||||||
TypeDetails t = new TypeDetails(CollectionStatus.SINGLETON, resourceName);
|
TypeDetails t = new TypeDetails(CollectionStatus.SINGLETON, resourceName);
|
||||||
|
|
||||||
if (viewDefinition.has("forEach")) {
|
|
||||||
checkForEach(path, viewDefinition, viewDefinition.get("forEach"), t);
|
|
||||||
} else if (viewDefinition.has("forEachOrNull")) {
|
|
||||||
checkForEachOrNull(path, viewDefinition, viewDefinition.get("forEachOrNull"), t);
|
|
||||||
} else if (viewDefinition.has("unionAll")) {
|
|
||||||
checkUnion(path, viewDefinition, viewDefinition.get("unionAll"), t);
|
|
||||||
} else {
|
|
||||||
i = 0;
|
i = 0;
|
||||||
if (checkAllObjects(path, viewDefinition, "select")) {
|
if (checkAllObjects(path, viewDefinition, "select")) {
|
||||||
for (JsonObject select : viewDefinition.getJsonObjects("select")) {
|
for (JsonObject select : viewDefinition.getJsonObjects("select")) {
|
||||||
checkSelect(path+".select["+i+"]", select, t);
|
columns.addAll(checkSelect(path+".select["+i+"]", select, t));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
|
@ -122,9 +117,11 @@ public class Validator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void checkSelect(String path, JsonObject select, TypeDetails t) {
|
private List<Column> checkSelect(String path, JsonObject select, TypeDetails t) {
|
||||||
|
List<Column> columns = new ArrayList<>();
|
||||||
|
select.setUserData("columns", columns);
|
||||||
|
checkProperties(select, path, "column", "select", "forEach", "forEachOrNull", "unionAll");
|
||||||
|
|
||||||
if (select.has("forEach")) {
|
if (select.has("forEach")) {
|
||||||
t = checkForEach(path, select, select.get("forEach"), t);
|
t = checkForEach(path, select, select.get("forEach"), t);
|
||||||
|
@ -133,24 +130,18 @@ public class Validator {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
boolean content = false;
|
|
||||||
|
|
||||||
if (select.has("unionAll")) {
|
|
||||||
content = checkUnion(path, select, select.get("unionAll"), t);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (select.has("column")) {
|
if (select.has("column")) {
|
||||||
JsonElement a = select.get("column");
|
JsonElement a = select.get("column");
|
||||||
if (!(a instanceof JsonArray)) {
|
if (!(a instanceof JsonArray)) {
|
||||||
error(path+".column", a, "column is not an array", IssueType.INVALID);
|
error(path+".column", a, "column is not an array", IssueType.INVALID);
|
||||||
} else {
|
} else {
|
||||||
content = true;
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (JsonElement e : ((JsonArray) a)) {
|
for (JsonElement e : ((JsonArray) a)) {
|
||||||
if (!(e instanceof JsonObject)) {
|
if (!(e instanceof JsonObject)) {
|
||||||
error(path+".column["+i+"]", a, "column["+i+"] is a "+e.type().toName()+" not an object", IssueType.INVALID);
|
error(path+".column["+i+"]", a, "column["+i+"] is a "+e.type().toName()+" not an object", IssueType.INVALID);
|
||||||
} else {
|
} else {
|
||||||
checkColumn(path+".column["+i+"]", (JsonObject) e, t);
|
columns.add(checkColumn(path+".column["+i+"]", (JsonObject) e, t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,47 +152,98 @@ public class Validator {
|
||||||
if (!(a instanceof JsonArray)) {
|
if (!(a instanceof JsonArray)) {
|
||||||
error(path+".select", a, "select is not an array", IssueType.INVALID);
|
error(path+".select", a, "select is not an array", IssueType.INVALID);
|
||||||
} else {
|
} else {
|
||||||
content = true;
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (JsonElement e : ((JsonArray) a)) {
|
for (JsonElement e : ((JsonArray) a)) {
|
||||||
if (!(e instanceof JsonObject)) {
|
if (!(e instanceof JsonObject)) {
|
||||||
error(path+".select["+i+"]", e, "select["+i+"] is not an object", IssueType.INVALID);
|
error(path+".select["+i+"]", e, "select["+i+"] is not an object", IssueType.INVALID);
|
||||||
} else {
|
} else {
|
||||||
checkSelect(path+".select["+i+"]", (JsonObject) e, t);
|
columns.addAll(checkSelect(path+".select["+i+"]", (JsonObject) e, t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!content) {
|
if (select.has("unionAll")) {
|
||||||
|
columns.addAll(checkUnion(path, select, select.get("unionAll"), t));
|
||||||
|
}
|
||||||
|
if (columns.isEmpty()) {
|
||||||
error(path, select, "The select has no columns or selects", IssueType.REQUIRED);
|
error(path, select, "The select has no columns or selects", IssueType.REQUIRED);
|
||||||
|
} else {
|
||||||
|
checkColumnNamesUnique(select, path, columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void checkColumnNamesUnique(JsonObject select, String path, List<Column> columns) {
|
||||||
|
Set<String> names = new HashSet<>();
|
||||||
|
for (Column col : columns) {
|
||||||
|
if (col != null) {
|
||||||
|
if (!names.contains(col.getName())) {
|
||||||
|
names.add(col.getName());
|
||||||
|
} else if (!col.isDuplicateReported()) {
|
||||||
|
col.setDuplicateReported(true);
|
||||||
|
error(path, select, "Duplicate Column Name '"+col.getName()+"'", IssueType.BUSINESSRULE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Column> checkUnion(String path, JsonObject focus, JsonElement expression, TypeDetails t) {
|
||||||
private boolean checkUnion(String path, JsonObject focus, JsonElement expression, TypeDetails t) {
|
|
||||||
JsonElement a = focus.get("unionAll");
|
JsonElement a = focus.get("unionAll");
|
||||||
if (!(a instanceof JsonArray)) {
|
if (!(a instanceof JsonArray)) {
|
||||||
error(path+".union", a, "union is not an array", IssueType.INVALID);
|
error(path+".unionAll", a, "union is not an array", IssueType.INVALID);
|
||||||
return false;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
List<List<Column>> unionColumns = new ArrayList<>();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (JsonElement e : ((JsonArray) a)) {
|
for (JsonElement e : ((JsonArray) a)) {
|
||||||
if (!(e instanceof JsonObject)) {
|
if (!(e instanceof JsonObject)) {
|
||||||
error(path+".union["+i+"]", e, "union["+i+"] is not an object", IssueType.INVALID);
|
error(path+".unionAll["+i+"]", e, "unionAll["+i+"] is not an object", IssueType.INVALID);
|
||||||
} else {
|
} else {
|
||||||
checkSelect(path+".union["+i+"]", (JsonObject) e, t);
|
unionColumns.add(checkSelect(path+".unionAll["+i+"]", (JsonObject) e, t));
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
if (i < 2) {
|
if (i < 2) {
|
||||||
warning(path+".union", a, "union should have more than one item");
|
warning(path+".unionAll", a, "unionAll should have more than one item");
|
||||||
}
|
}
|
||||||
return true;
|
if (unionColumns.size() > 1) {
|
||||||
|
List<Column> columns = unionColumns.get(0);
|
||||||
|
for (int ic = 1; ic < unionColumns.size(); ic++) {
|
||||||
|
String diff = columnDiffs(columns, unionColumns.get(ic));
|
||||||
|
if (diff != null) {
|
||||||
|
error(path+".unionAll["+i+"]", ((JsonArray) a).get(ic), "unionAll["+i+"] column definitions do not match: "+diff, IssueType.INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.setUserData("colunms", columns);
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String columnDiffs(List<Column> list1, List<Column> list2) {
|
||||||
|
if (list1.size() == list2.size()) {
|
||||||
|
for (int i = 0; i < list1.size(); i++) {
|
||||||
|
if (list1.get(i) == null || list2.get(i) == null) {
|
||||||
|
return null; // just suppress any addition errors
|
||||||
|
}
|
||||||
|
String diff = list1.get(i).diff(list2.get(i));
|
||||||
|
if (diff != null) {
|
||||||
|
return diff+" at #"+i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return "Column counts differ: "+list1.size()+" vs "+list2.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkColumn(String path, JsonObject column, TypeDetails t) {
|
private Column checkColumn(String path, JsonObject column, TypeDetails t) {
|
||||||
|
checkProperties(column, path, "path", "name", "description", "collection", "type", "tag");
|
||||||
|
|
||||||
if (!column.has("path")) {
|
if (!column.has("path")) {
|
||||||
error(path, column, "no path found", IssueType.INVALID);
|
error(path, column, "no path found", IssueType.INVALID);
|
||||||
} else {
|
} else {
|
||||||
|
@ -253,7 +295,7 @@ public class Validator {
|
||||||
// ok, name is sorted!
|
// ok, name is sorted!
|
||||||
if (columnName != null) {
|
if (columnName != null) {
|
||||||
column.setUserData("name", columnName);
|
column.setUserData("name", columnName);
|
||||||
boolean isColl = (td.getCollectionStatus() != CollectionStatus.SINGLETON) || column(columnName) != null;
|
boolean isColl = (td.getCollectionStatus() != CollectionStatus.SINGLETON);
|
||||||
if (column.has("collection")) {
|
if (column.has("collection")) {
|
||||||
JsonElement collectionJ = column.get("collection");
|
JsonElement collectionJ = column.get("collection");
|
||||||
if (!(collectionJ instanceof JsonBoolean)) {
|
if (!(collectionJ instanceof JsonBoolean)) {
|
||||||
|
@ -273,8 +315,11 @@ public class Validator {
|
||||||
warning(path, expression, "column appears to be a collection, but this is not allowed in this context");
|
warning(path, expression, "column appears to be a collection, but this is not allowed in this context");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ok collection is sorted
|
|
||||||
Set<String> types = new HashSet<>();
|
Set<String> types = new HashSet<>();
|
||||||
|
if (node.isNullSet()) {
|
||||||
|
types.add("null");
|
||||||
|
} else {
|
||||||
|
// ok collection is sorted
|
||||||
for (String type : td.getTypes()) {
|
for (String type : td.getTypes()) {
|
||||||
types.add(simpleType(type));
|
types.add(simpleType(type));
|
||||||
}
|
}
|
||||||
|
@ -293,12 +338,13 @@ public class Validator {
|
||||||
error(path+".type", typeJ, "type must be a string", IssueType.INVALID);
|
error(path+".type", typeJ, "type must be a string", IssueType.INVALID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (types.size() != 1) {
|
if (types.size() != 1) {
|
||||||
error(path, column, "Unable to determine a type (found "+td.describe()+")", IssueType.BUSINESSRULE);
|
error(path, column, "Unable to determine a type (found "+td.describe()+")", IssueType.BUSINESSRULE);
|
||||||
} else {
|
} else {
|
||||||
String type = types.iterator().next();
|
String type = types.iterator().next();
|
||||||
boolean ok = false;
|
boolean ok = false;
|
||||||
if (!isSimpleType(type)) {
|
if (!isSimpleType(type) && !"null".equals(type)) {
|
||||||
if (complexTypes) {
|
if (complexTypes) {
|
||||||
warning(path, expression, "Column is a complex type. This is not supported in some Runners");
|
warning(path, expression, "Column is a complex type. This is not supported in some Runners");
|
||||||
} else if (!complexTypes) {
|
} else if (!complexTypes) {
|
||||||
|
@ -310,28 +356,21 @@ public class Validator {
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
if (ok) {
|
if (ok) {
|
||||||
Column col = column(columnName);
|
Column col = new Column(columnName, isColl, type, kindForType(type));
|
||||||
if (col != null) {
|
column.setUserData("column", col);
|
||||||
if (!col.getType().equals(type)) {
|
return col;
|
||||||
error(path, expression, "Duplicate definition for "+columnName+" has different types ("+col.getType()+" vs "+type+")", IssueType.BUSINESSRULE);
|
|
||||||
}
|
|
||||||
if (col.isColl() != isColl) {
|
|
||||||
error(path, expression, "Duplicate definition for "+columnName+" has different status for collection ("+col.isColl()+" vs "+isColl+")", IssueType.BUSINESSRULE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
columns.add(new Column(columnName, isColl, type, kindForType(type)));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ColumnKind kindForType(String type) {
|
private ColumnKind kindForType(String type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case "null": return ColumnKind.Null;
|
||||||
case "dateTime": return ColumnKind.DateTime;
|
case "dateTime": return ColumnKind.DateTime;
|
||||||
case "boolean": return ColumnKind.Boolean;
|
case "boolean": return ColumnKind.Boolean;
|
||||||
case "integer": return ColumnKind.Integer;
|
case "integer": return ColumnKind.Integer;
|
||||||
|
@ -343,15 +382,6 @@ public class Validator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Column column(String columnName) {
|
|
||||||
for (Column t : columns) {
|
|
||||||
if (t.getName().equalsIgnoreCase(columnName)) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSimpleType(String type) {
|
private boolean isSimpleType(String type) {
|
||||||
return Utilities.existsInList(type, "dateTime", "boolean", "integer", "decimal", "string", "base64Binary");
|
return Utilities.existsInList(type, "dateTime", "boolean", "integer", "decimal", "string", "base64Binary");
|
||||||
}
|
}
|
||||||
|
@ -433,6 +463,7 @@ public class Validator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkConstant(String path, JsonObject constant) {
|
private void checkConstant(String path, JsonObject constant) {
|
||||||
|
checkProperties(constant, path, "name", "valueBase64Binary", "valueBoolean", "valueCanonical", "valueCode", "valueDate", "valueDateTime", "valueDecimal", "valueId", "valueInstant", "valueInteger", "valueInteger64", "valueOid", "valueString", "valuePositiveInt", "valueTime", "valueUnsignedInt", "valueUri", "valueUrl", "valueUuid");
|
||||||
JsonElement nameJ = constant.get("name");
|
JsonElement nameJ = constant.get("name");
|
||||||
if (nameJ == null) {
|
if (nameJ == null) {
|
||||||
error(path, constant, "No name provided", IssueType.REQUIRED);
|
error(path, constant, "No name provided", IssueType.REQUIRED);
|
||||||
|
@ -508,6 +539,8 @@ public class Validator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void checkWhere(String path, JsonObject where) {
|
private void checkWhere(String path, JsonObject where) {
|
||||||
|
checkProperties(where, path, "path", "description");
|
||||||
|
|
||||||
String expr = where.asString("path");
|
String expr = where.asString("path");
|
||||||
if (expr == null) {
|
if (expr == null) {
|
||||||
error(path, where, "No path provided", IssueType.REQUIRED);
|
error(path, where, "No path provided", IssueType.REQUIRED);
|
||||||
|
@ -534,6 +567,19 @@ public class Validator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkProperties(JsonObject obj, String path, String... names) {
|
||||||
|
for (JsonProperty p : obj.getProperties()) {
|
||||||
|
boolean nameOk = "extension".equals(p.getName());
|
||||||
|
for (String name : names) {
|
||||||
|
nameOk = nameOk || name.equals(p.getName());
|
||||||
|
}
|
||||||
|
if (!nameOk) {
|
||||||
|
error(path+"."+p.getName(), p.getValue(), "Unknown JSON "+p.getValue().type().name(), IssueType.UNKNOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isValidName(String name) {
|
private boolean isValidName(String name) {
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
for (char c : name.toCharArray()) {
|
for (char c : name.toCharArray()) {
|
||||||
|
|
|
@ -1,24 +1,37 @@
|
||||||
package org.hl7.fhir.r5.utils.validation;
|
package org.hl7.fhir.r5.utils.validation;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
public class BundleValidationRule {
|
public class BundleValidationRule {
|
||||||
|
@JsonProperty("rule")
|
||||||
private String rule;
|
private String rule;
|
||||||
|
|
||||||
|
@JsonProperty("profile")
|
||||||
private String profile;
|
private String profile;
|
||||||
private boolean checked;
|
private boolean checked;
|
||||||
|
|
||||||
public BundleValidationRule(String rule, String profile) {
|
@JsonProperty("rule")
|
||||||
super();
|
|
||||||
this.rule = rule;
|
|
||||||
this.profile = profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonProperty("rule")
|
||||||
|
public BundleValidationRule setRule(String rule) {
|
||||||
|
this.rule = rule;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("profile")
|
||||||
public String getProfile() {
|
public String getProfile() {
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonProperty("profile")
|
||||||
|
public BundleValidationRule setProfile(String profile) {
|
||||||
|
this.profile = profile;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isChecked() {
|
public boolean isChecked() {
|
||||||
return checked;
|
return checked;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class SQLOnFhirTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Base resolveReference(String ref, String rt) {
|
public Base resolveReference(Base rootResource, String ref, String rt) {
|
||||||
if (ref == null) {
|
if (ref == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,7 @@ public class SQLOnFhirTests {
|
||||||
runner.execute(test.path+".view", test.testCase.getJsonObject("view"));
|
runner.execute(test.path+".view", test.testCase.getJsonObject("view"));
|
||||||
results = store.getRows();
|
results = store.getRows();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
Assertions.assertTrue(test.testCase.has("expectError"), e.getMessage());
|
Assertions.assertTrue(test.testCase.has("expectError"), e.getMessage());
|
||||||
}
|
}
|
||||||
if (results != null) {
|
if (results != null) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ public class HL7WorkGroups {
|
||||||
case "sd": return "Structured Documents";
|
case "sd": return "Structured Documents";
|
||||||
case "sec": return "Security";
|
case "sec": return "Security";
|
||||||
case "soa": return "Services Oriented Architecture";
|
case "soa": return "Services Oriented Architecture";
|
||||||
case "ti": return "Terminology Infrastructure Work Group";
|
case "ti": return "Terminology Infrastructure";
|
||||||
case "tsmg": return "Terminology Services Management Group (TSMG)";
|
case "tsmg": return "Terminology Services Management Group (TSMG)";
|
||||||
case "us": return "US Realm Steering Committee";
|
case "us": return "US Realm Steering Committee";
|
||||||
case "v2": return "V2 Management Group";
|
case "v2": return "V2 Management Group";
|
||||||
|
|
|
@ -251,14 +251,10 @@ public class VersionUtilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSemVer(String version) {
|
public static boolean isSemVer(String version) {
|
||||||
if (Utilities.charCount(version, '.') != 2) {
|
if (Utilities.noString(version)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String[] p = version.split("\\.");
|
return version.matches("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-\\+]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-\\+][0-9a-zA-Z-\\+]*))*))?$");
|
||||||
if (p[2].contains("-")) {
|
|
||||||
p[2] = p[2].substring(0, p[2].indexOf("-"));
|
|
||||||
}
|
|
||||||
return Utilities.isInteger(p[0]) && Utilities.isInteger(p[1]) && Utilities.isInteger(p[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1012,6 +1012,8 @@ public class I18nConstants {
|
||||||
public static final String TERMINOLOGY_TX_OID_MULTIPLE_MATCHES = "TERMINOLOGY_TX_OID_MULTIPLE_MATCHES";
|
public static final String TERMINOLOGY_TX_OID_MULTIPLE_MATCHES = "TERMINOLOGY_TX_OID_MULTIPLE_MATCHES";
|
||||||
public static final String CDA_UNKNOWN_TEMPLATE = "CDA_UNKNOWN_TEMPLATE";
|
public static final String CDA_UNKNOWN_TEMPLATE = "CDA_UNKNOWN_TEMPLATE";
|
||||||
public static final String CDA_UNKNOWN_TEMPLATE_EXT = "CDA_UNKNOWN_TEMPLATE_EXT";
|
public static final String CDA_UNKNOWN_TEMPLATE_EXT = "CDA_UNKNOWN_TEMPLATE_EXT";
|
||||||
|
public static final String UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV = "UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV";
|
||||||
|
public static final String ED_CONTEXT_INVARIANT_EXPRESSION_ERROR = "ED_CONTEXT_INVARIANT_EXPRESSION_ERROR";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ public abstract class BasePackageCacheManager implements IPackageCacheManager {
|
||||||
// this is not a long term thing, but it's not clear how to release patches for
|
// this is not a long term thing, but it's not clear how to release patches for
|
||||||
// 1.4.0
|
// 1.4.0
|
||||||
private boolean okToUsePackageServer(String server, String id) {
|
private boolean okToUsePackageServer(String server, String id) {
|
||||||
if ("http://packages.fhir.org".equals(server) && "hl7.fhir.r2b.core".equals(id)) {
|
if (PackageServer.PRIMARY_SERVER.equals(server) && "hl7.fhir.r2b.core".equals(id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -119,7 +119,6 @@ public class PackageClient {
|
||||||
try {
|
try {
|
||||||
return obj.hasString("date") ? obj.asDate("date") : null;
|
return obj.hasString("date") ? obj.asDate("date") : null;
|
||||||
} catch (DateTimeParseException e) {
|
} catch (DateTimeParseException e) {
|
||||||
//FIXME Some IGs use an older date format:
|
|
||||||
try {
|
try {
|
||||||
return new SimpleDateFormat("yyyyMMddhhmmss").parse(obj.getJsonString("date").asString()).toInstant();
|
return new SimpleDateFormat("yyyyMMddhhmmss").parse(obj.getJsonString("date").asString()).toInstant();
|
||||||
} catch (ParseException ex) {
|
} catch (ParseException ex) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class PackageServer {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String PRIMARY_SERVER = "http://packages.fhir.org";
|
public static final String PRIMARY_SERVER = "https://packages.fhir.org";
|
||||||
public static final String SECONDARY_SERVER = "https://packages2.fhir.org/packages";
|
public static final String SECONDARY_SERVER = "https://packages2.fhir.org/packages";
|
||||||
|
|
||||||
public static PackageServer primaryServer() {
|
public static PackageServer primaryServer() {
|
||||||
|
|
|
@ -610,7 +610,7 @@ FHIRPATH_PRIMITIVE_ONLY = Error evaluating FHIRPath expression: The function {0}
|
||||||
FHIRPATH_REFERENCE_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered string, uri, canonical or Reference but found {1}
|
FHIRPATH_REFERENCE_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered string, uri, canonical or Reference but found {1}
|
||||||
FHIRPATH_CODED_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered string, code, uri, Coding, CodeableConcept but found {1}
|
FHIRPATH_CODED_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered string, code, uri, Coding, CodeableConcept but found {1}
|
||||||
FHIRPATH_STRING_ORD_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered collection of string, uri, code, id but found {1}
|
FHIRPATH_STRING_ORD_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered collection of string, uri, code, id but found {1}
|
||||||
FHIRPATH_STRING_SING_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on string, uri, code, id but found {1}
|
FHIRPATH_STRING_SING_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on string, uri, url, code, id but found {1}
|
||||||
FHIRPATH_NO_COLLECTION = Error evaluating FHIRPath expression: The function {0} can only be used on a singleton value but found {1}
|
FHIRPATH_NO_COLLECTION = Error evaluating FHIRPath expression: The function {0} can only be used on a singleton value but found {1}
|
||||||
FHIRPATH_NOT_IMPLEMENTED = Error evaluating FHIRPath expression: The function {0} is not implemented
|
FHIRPATH_NOT_IMPLEMENTED = Error evaluating FHIRPath expression: The function {0} is not implemented
|
||||||
FHIRPATH_PARAM_WRONG = Error evaluating FHIRPath expression: The expression type {0} is not supported for parameter {1} on function {2}
|
FHIRPATH_PARAM_WRONG = Error evaluating FHIRPath expression: The expression type {0} is not supported for parameter {1} on function {2}
|
||||||
|
@ -928,7 +928,7 @@ NO_VALID_DISPLAY_FOUND_one = No valid Display Names found for {1}#{2} in the lan
|
||||||
NO_VALID_DISPLAY_FOUND_other = No valid Display Names found for {1}#{2} in the languages {4}
|
NO_VALID_DISPLAY_FOUND_other = No valid Display Names found for {1}#{2} in the languages {4}
|
||||||
SD_NO_CONTEXT_WHEN_NOT_EXTENSION = The type is {0} so an extension context should not be specified
|
SD_NO_CONTEXT_WHEN_NOT_EXTENSION = The type is {0} so an extension context should not be specified
|
||||||
SD_NO_CONTEXT_INV_WHEN_NOT_EXTENSION = The type is {0} so an extension context invariants should not be specified
|
SD_NO_CONTEXT_INV_WHEN_NOT_EXTENSION = The type is {0} so an extension context invariants should not be specified
|
||||||
SD_CONTEXT_SHOULD_NOT_BE_ELEMENT = Review the extension type: extensions should not have a context of {0} unless it''s really intended that they can be used anywhere
|
SD_CONTEXT_SHOULD_NOT_BE_ELEMENT = Review the extension type for {1}: extensions should not have a context of {0} unless it''s really intended that they can be used anywhere
|
||||||
ED_PATH_WRONG_TYPE_MATCH = The path must be ''{0}'' not ''{1}'' when the type list is not constrained
|
ED_PATH_WRONG_TYPE_MATCH = The path must be ''{0}'' not ''{1}'' when the type list is not constrained
|
||||||
ATTEMPT_TO_CHANGE_SLICING = The element at {0} defines the slicing {1} but then an element in the slicing {2} tries to redefine the slicing to {3}
|
ATTEMPT_TO_CHANGE_SLICING = The element at {0} defines the slicing {1} but then an element in the slicing {2} tries to redefine the slicing to {3}
|
||||||
REPEAT_SLICING_IGNORED = The element at {0} defines the slicing but then an element in the slicing {2} repeats it, which is ignored
|
REPEAT_SLICING_IGNORED = The element at {0} defines the slicing but then an element in the slicing {2} repeats it, which is ignored
|
||||||
|
@ -1069,3 +1069,5 @@ XSI_TYPE_UNNECESSARY = xsi:type is unnecessary at this point
|
||||||
TERMINOLOGY_TX_OID_MULTIPLE_MATCHES = The OID ''{0}'' matches multiple code systems ({1})
|
TERMINOLOGY_TX_OID_MULTIPLE_MATCHES = The OID ''{0}'' matches multiple code systems ({1})
|
||||||
CDA_UNKNOWN_TEMPLATE = The CDA Template {0} is not known
|
CDA_UNKNOWN_TEMPLATE = The CDA Template {0} is not known
|
||||||
CDA_UNKNOWN_TEMPLATE_EXT = The CDA Template {0} / {1} is not known
|
CDA_UNKNOWN_TEMPLATE_EXT = The CDA Template {0} / {1} is not known
|
||||||
|
UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV = The types could not be determined from the extension context, so the invariant can't be validated (types = {0})
|
||||||
|
ED_CONTEXT_INVARIANT_EXPRESSION_ERROR = Error in constraint ''{0}'': {1}
|
|
@ -11,6 +11,7 @@ public class VersionUtilitiesTest {
|
||||||
public void isValidSemVer() {
|
public void isValidSemVer() {
|
||||||
assertTrue(VersionUtilities.isSemVer("0.1.1"));
|
assertTrue(VersionUtilities.isSemVer("0.1.1"));
|
||||||
assertTrue(VersionUtilities.isSemVer("0.1.1-ballot1"));
|
assertTrue(VersionUtilities.isSemVer("0.1.1-ballot1"));
|
||||||
|
assertTrue(VersionUtilities.isSemVer("0.0.0-alpha.0.131"));
|
||||||
assertFalse(VersionUtilities.isSemVer("0.1.a"));
|
assertFalse(VersionUtilities.isSemVer("0.1.a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -351,7 +351,6 @@ public class BaseValidator implements IValidationContextResourceLoader {
|
||||||
* Set this parameter to <code>false</code> if the validation does not pass
|
* Set this parameter to <code>false</code> if the validation does not pass
|
||||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||||
*/
|
*/
|
||||||
//FIXME: formatMessage should be done here
|
|
||||||
protected boolean slicingHint(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, boolean isCritical, String msg, String html, String[] text) {
|
protected boolean slicingHint(List<ValidationMessage> errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, boolean isCritical, String msg, String html, String[] text) {
|
||||||
if (!thePass && doingHints()) {
|
if (!thePass && doingHints()) {
|
||||||
addValidationMessage(errors, ruleDate, type, line, col, path, msg, IssueSeverity.INFORMATION, null).setSlicingHint(true).setSliceHtml(html, text).setCriticalSignpost(isCritical);
|
addValidationMessage(errors, ruleDate, type, line, col, path, msg, IssueSeverity.INFORMATION, null).setSlicingHint(true).setSliceHtml(html, text).setCriticalSignpost(isCritical);
|
||||||
|
|
|
@ -132,7 +132,7 @@ public class CliContext {
|
||||||
@JsonProperty("outputStyle")
|
@JsonProperty("outputStyle")
|
||||||
private String outputStyle = null;
|
private String outputStyle = null;
|
||||||
|
|
||||||
// TODO: Mark what goes here?
|
@JsonProperty("bundleValidationRules")
|
||||||
private List<BundleValidationRule> bundleValidationRules = new ArrayList<>();
|
private List<BundleValidationRule> bundleValidationRules = new ArrayList<>();
|
||||||
|
|
||||||
@JsonProperty("jurisdiction")
|
@JsonProperty("jurisdiction")
|
||||||
|
@ -191,11 +191,17 @@ public class CliContext {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Mark what goes here?
|
@JsonProperty("bundleValidationRules")
|
||||||
public List<BundleValidationRule> getBundleValidationRules() {
|
public List<BundleValidationRule> getBundleValidationRules() {
|
||||||
return bundleValidationRules;
|
return bundleValidationRules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonProperty("bundleValidationRules")
|
||||||
|
public CliContext setBundleValidationRules(List<BundleValidationRule> bundleValidationRules) {
|
||||||
|
this.bundleValidationRules = bundleValidationRules;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CliContext addIg(String ig) {
|
public CliContext addIg(String ig) {
|
||||||
if (this.igs == null) {
|
if (this.igs == null) {
|
||||||
this.igs = new ArrayList<>();
|
this.igs = new ArrayList<>();
|
||||||
|
@ -322,14 +328,18 @@ public class CliContext {
|
||||||
this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath;
|
this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonProperty("checkIPSCodes")
|
||||||
public boolean isCheckIPSCodes() {
|
public boolean isCheckIPSCodes() {
|
||||||
return checkIPSCodes;
|
return checkIPSCodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCheckIPSCodes(boolean checkIPSCodes) {
|
@JsonProperty("checkIPSCodes")
|
||||||
|
public CliContext setCheckIPSCodes(boolean checkIPSCodes) {
|
||||||
this.checkIPSCodes = checkIPSCodes;
|
this.checkIPSCodes = checkIPSCodes;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@JsonProperty("locale")
|
@JsonProperty("locale")
|
||||||
public String getLanguageCode() {
|
public String getLanguageCode() {
|
||||||
return locale;
|
return locale;
|
||||||
|
|
|
@ -181,25 +181,25 @@ public class Params {
|
||||||
cliContext.addProfile(p);
|
cliContext.addProfile(p);
|
||||||
}
|
}
|
||||||
} else if (args[i].equals(BUNDLE)) {
|
} else if (args[i].equals(BUNDLE)) {
|
||||||
String p = null;
|
String profile = null;
|
||||||
String r = null;
|
String rule = null;
|
||||||
if (i + 1 == args.length) {
|
if (i + 1 == args.length) {
|
||||||
throw new Error("Specified -profile without indicating bundle rule ");
|
throw new Error("Specified -profile without indicating bundle rule ");
|
||||||
} else {
|
} else {
|
||||||
r = args[++i];
|
rule = args[++i];
|
||||||
}
|
}
|
||||||
if (i + 1 == args.length) {
|
if (i + 1 == args.length) {
|
||||||
throw new Error("Specified -profile without indicating profile source");
|
throw new Error("Specified -profile without indicating profile source");
|
||||||
} else {
|
} else {
|
||||||
p = args[++i];
|
profile = args[++i];
|
||||||
}
|
}
|
||||||
cliContext.getBundleValidationRules().add(new BundleValidationRule(r, p));
|
cliContext.getBundleValidationRules().add(new BundleValidationRule().setRule(rule).setProfile(profile));
|
||||||
} else if (args[i].equals(QUESTIONNAIRE)) {
|
} else if (args[i].equals(QUESTIONNAIRE)) {
|
||||||
if (i + 1 == args.length)
|
if (i + 1 == args.length)
|
||||||
throw new Error("Specified -questionnaire without indicating questionnaire mode");
|
throw new Error("Specified -questionnaire without indicating questionnaire mode");
|
||||||
else {
|
else {
|
||||||
String q = args[++i];
|
String questionnaireMode = args[++i];
|
||||||
cliContext.setQuestionnaireMode(QuestionnaireMode.fromCode(q));
|
cliContext.setQuestionnaireMode(QuestionnaireMode.fromCode(questionnaireMode));
|
||||||
}
|
}
|
||||||
} else if (args[i].equals(LEVEL)) {
|
} else if (args[i].equals(LEVEL)) {
|
||||||
if (i + 1 == args.length)
|
if (i + 1 == args.length)
|
||||||
|
|
|
@ -5871,10 +5871,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
if (!definition.getPath().contains(".") && profile.hasExtension(ToolingExtensions.EXT_PROFILE_STYLE) && "cda".equals(ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_PROFILE_STYLE))) {
|
if (!definition.getPath().contains(".") && profile.hasExtension(ToolingExtensions.EXT_PROFILE_STYLE) && "cda".equals(ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_PROFILE_STYLE))) {
|
||||||
List<Element> templates = element.getChildren("templateId");
|
List<Element> templates = element.getChildren("templateId");
|
||||||
for (Element t : templates) {
|
for (Element t : templates) {
|
||||||
String tid = "urn:hl7ii:"+t.getChildValue("root")+(t.hasChild("extension") ? ":"+t.getChildValue("extension") : "");
|
String tid = t.hasChild("extension") ? "urn:hl7ii:"+t.getChildValue("root")+ ":"+t.getChildValue("extension") : "urn:oid:"+t.getChildValue("root");
|
||||||
StructureDefinition sd = cu.fetchProfileByIdentifier(tid);
|
StructureDefinition sd = cu.fetchProfileByIdentifier(tid);
|
||||||
if (sd == null) {
|
if (sd == null) {
|
||||||
ok = rule(errors, "2023-10-20", IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, t.hasChild("extension") ? I18nConstants.CDA_UNKNOWN_TEMPLATE_EXT : I18nConstants.CDA_UNKNOWN_TEMPLATE, t.getChildValue("root"), t.getChildValue("extension")) && ok;
|
hint(errors, "2023-10-20", IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, t.hasChild("extension") ? I18nConstants.CDA_UNKNOWN_TEMPLATE_EXT : I18nConstants.CDA_UNKNOWN_TEMPLATE, t.getChildValue("root"), t.getChildValue("extension"));
|
||||||
} else {
|
} else {
|
||||||
ElementDefinition ed = sd.getSnapshot().getElementFirstRep();
|
ElementDefinition ed = sd.getSnapshot().getElementFirstRep();
|
||||||
if (!element.hasValidated(sd, ed)) {
|
if (!element.hasValidated(sd, ed)) {
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.hl7.fhir.r5.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
|
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
|
||||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||||
|
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
import org.hl7.fhir.utilities.VersionUtilities;
|
import org.hl7.fhir.utilities.VersionUtilities;
|
||||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||||
|
@ -46,6 +47,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||||
import org.hl7.fhir.validation.BaseValidator;
|
import org.hl7.fhir.validation.BaseValidator;
|
||||||
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||||
|
import org.hl7.fhir.validation.instance.utils.ValidationContext;
|
||||||
|
|
||||||
public class StructureDefinitionValidator extends BaseValidator {
|
public class StructureDefinitionValidator extends BaseValidator {
|
||||||
|
|
||||||
|
@ -152,6 +154,7 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Element> extensions = src.getChildren("extension");
|
List<Element> extensions = src.getChildren("extension");
|
||||||
int c = 0;
|
int c = 0;
|
||||||
for (Element extension : extensions) {
|
for (Element extension : extensions) {
|
||||||
|
@ -161,6 +164,13 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
c++;
|
c++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Element> contextInvariants = src.getChildren("contextInvariant");
|
||||||
|
c = 0;
|
||||||
|
for (Element contextInvariant : contextInvariants) {
|
||||||
|
ok = validateContextInvariant(errors, contextInvariant, src, stack.push(contextInvariant, c, null, null)) && ok;
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
|
||||||
// if this is defining an extension, make sure that the extension fixed value matches the URL
|
// if this is defining an extension, make sure that the extension fixed value matches the URL
|
||||||
String type = src.getNamedChildValue("type");
|
String type = src.getNamedChildValue("type");
|
||||||
if ("Extension".equals(type)) {
|
if ("Extension".equals(type)) {
|
||||||
|
@ -344,7 +354,7 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
cv = ec.primitiveValue();
|
cv = ec.primitiveValue();
|
||||||
}
|
}
|
||||||
if ("element".equals(ct) && "Element".equals(cv)) {
|
if ("element".equals(ct) && "Element".equals(cv)) {
|
||||||
warning(errors, "2023-04-23", IssueType.BUSINESSRULE, n.getLiteralPath(), false, I18nConstants.SD_CONTEXT_SHOULD_NOT_BE_ELEMENT, cv);
|
warning(errors, "2023-04-23", IssueType.BUSINESSRULE, n.getLiteralPath(), false, I18nConstants.SD_CONTEXT_SHOULD_NOT_BE_ELEMENT, cv, src.getNamedChildValue("id"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ok = rule(errors, "2023-04-23", IssueType.INVALID, n.getLiteralPath(), false, I18nConstants.SD_NO_CONTEXT_WHEN_NOT_EXTENSION, type) && ok;
|
ok = rule(errors, "2023-04-23", IssueType.INVALID, n.getLiteralPath(), false, I18nConstants.SD_NO_CONTEXT_WHEN_NOT_EXTENSION, type) && ok;
|
||||||
|
@ -545,10 +555,11 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
types.add(elements.get(0).getNamedChildValue("path"));
|
types.add(elements.get(0).getNamedChildValue("path"));
|
||||||
}
|
}
|
||||||
List<String> warnings = new ArrayList<>();
|
List<String> warnings = new ArrayList<>();
|
||||||
|
ValidationContext vc = new ValidationContext(invariant);
|
||||||
if (Utilities.existsInList(rootPath, context.getResourceNames())) {
|
if (Utilities.existsInList(rootPath, context.getResourceNames())) {
|
||||||
fpe.checkOnTypes(invariant, rootPath, types, fpe.parse(exp), warnings);
|
fpe.checkOnTypes(vc, rootPath, types, fpe.parse(exp), warnings);
|
||||||
} else {
|
} else {
|
||||||
fpe.checkOnTypes(invariant, "DomainResource", types, fpe.parse(exp), warnings);
|
fpe.checkOnTypes(vc, "DomainResource", types, fpe.parse(exp), warnings);
|
||||||
}
|
}
|
||||||
for (String s : warnings) {
|
for (String s : warnings) {
|
||||||
warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, false, key+": "+s);
|
warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, false, key+": "+s);
|
||||||
|
@ -571,6 +582,61 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean validateContextInvariant(List<ValidationMessage> errors, Element invariant, Element sd, NodeStack stack) {
|
||||||
|
boolean ok = true;
|
||||||
|
String expression = invariant.getValue();
|
||||||
|
if (!Utilities.noString(expression)) {
|
||||||
|
// we have to figure out the context, and we might be in type slicing.
|
||||||
|
String exp = expression;
|
||||||
|
List<String> types = listTypeContexts(sd);
|
||||||
|
if (types.size() == 0) {
|
||||||
|
hint(errors, "2023-10-31", IssueType.INFORMATIONAL, stack, false, I18nConstants.UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV, listContexts(sd));
|
||||||
|
} else
|
||||||
|
try {
|
||||||
|
List<String> warnings = new ArrayList<>();
|
||||||
|
ValidationContext vc = new ValidationContext(invariant);
|
||||||
|
fpe.checkOnTypes(vc, "DomainResource", types, fpe.parse(exp), warnings);
|
||||||
|
for (String s : warnings) {
|
||||||
|
warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, false, s);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (debug) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
ok = rule(errors, "2023-06-19", IssueType.INVALID, stack, false, I18nConstants.ED_CONTEXT_INVARIANT_EXPRESSION_ERROR, expression, e.getMessage()) && ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object listContexts(Element sd) {
|
||||||
|
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||||
|
for (Element e : sd.getChildren("context")) {
|
||||||
|
b.append(e.getNamedChildValue("type")+"="+e.getNamedChildValue("expression"));
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> listTypeContexts(Element sd) {
|
||||||
|
List<String> types = new ArrayList<>();
|
||||||
|
for (Element e : sd.getChildren("context")) {
|
||||||
|
switch (e.getNamedChildValue("type")) {
|
||||||
|
case "fhirpath" :
|
||||||
|
break;
|
||||||
|
case "element" :
|
||||||
|
types.add(e.getNamedChildValue("expression"));
|
||||||
|
break;
|
||||||
|
case "extension" :
|
||||||
|
// this isn't defined?
|
||||||
|
types.add(e.getNamedChildValue("Extension"));
|
||||||
|
types.add(e.getNamedChildValue("Extension.value"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean haseHasInvariant(StructureDefinition base, String key) {
|
private boolean haseHasInvariant(StructureDefinition base, String key) {
|
||||||
for (ElementDefinition ed : base.getSnapshot().getElement()) {
|
for (ElementDefinition ed : base.getSnapshot().getElement()) {
|
||||||
for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) {
|
for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) {
|
||||||
|
|
|
@ -287,7 +287,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||||
}
|
}
|
||||||
val.getBundleValidationRules().clear();
|
val.getBundleValidationRules().clear();
|
||||||
if (content.has("bundle-param")) {
|
if (content.has("bundle-param")) {
|
||||||
val.getBundleValidationRules().add(new BundleValidationRule(content.getAsJsonObject("bundle-param").get("rule").getAsString(), content.getAsJsonObject("bundle-param").get("profile").getAsString()));
|
val.getBundleValidationRules().add(new BundleValidationRule().setRule(content.getAsJsonObject("bundle-param").get("rule").getAsString()).setProfile( content.getAsJsonObject("bundle-param").get("profile").getAsString()));
|
||||||
}
|
}
|
||||||
if (content.has("profiles")) {
|
if (content.has("profiles")) {
|
||||||
for (JsonElement je : content.getAsJsonArray("profiles")) {
|
for (JsonElement je : content.getAsJsonArray("profiles")) {
|
||||||
|
|
4
pom.xml
4
pom.xml
|
@ -14,13 +14,13 @@
|
||||||
HAPI FHIR
|
HAPI FHIR
|
||||||
-->
|
-->
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
<version>6.2.1-SNAPSHOT</version>
|
<version>6.2.2-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<guava_version>32.0.1-jre</guava_version>
|
<guava_version>32.0.1-jre</guava_version>
|
||||||
<hapi_fhir_version>6.4.1</hapi_fhir_version>
|
<hapi_fhir_version>6.4.1</hapi_fhir_version>
|
||||||
<validator_test_case_version>1.4.13</validator_test_case_version>
|
<validator_test_case_version>1.4.14</validator_test_case_version>
|
||||||
<jackson_version>2.15.2</jackson_version>
|
<jackson_version>2.15.2</jackson_version>
|
||||||
<junit_jupiter_version>5.9.2</junit_jupiter_version>
|
<junit_jupiter_version>5.9.2</junit_jupiter_version>
|
||||||
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>
|
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>
|
||||||
|
|
Loading…
Reference in New Issue