Merge pull request #1721 from hapifhir/2024-08-gg-misc

2024 08 gg misc
This commit is contained in:
Grahame Grieve 2024-08-27 07:58:48 +08:00 committed by GitHub
commit be52ad2e96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 610 additions and 285 deletions

View File

@ -1,5 +1,7 @@
package org.hl7.fhir.convertors.conv40_50.datatypes40_50;
import java.util.Arrays;
import org.hl7.fhir.convertors.context.ConversionContext40_50;
import org.hl7.fhir.convertors.conv40_50.datatypes40_50.special40_50.Extension40_50;
import org.hl7.fhir.exceptions.FHIRException;
@ -8,14 +10,22 @@ public class BackboneElement40_50 {
public static void copyBackboneElement(org.hl7.fhir.r4.model.BackboneElement src, org.hl7.fhir.r5.model.BackboneElement tgt, String ... extensionUrlsToIgnore) throws FHIRException {
ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt, extensionUrlsToIgnore);
for (org.hl7.fhir.r4.model.Extension e : src.getModifierExtension()) {
tgt.addModifierExtension(Extension40_50.convertExtension(e));
if (!isExemptExtension(e.getUrl(), extensionUrlsToIgnore)) {
tgt.addModifierExtension(Extension40_50.convertExtension(e));
}
}
}
public static boolean isExemptExtension(String url, String[] extensionsToIgnore) {
return Arrays.asList(extensionsToIgnore).contains(url);
}
public static void copyBackboneElement(org.hl7.fhir.r5.model.BackboneElement src, org.hl7.fhir.r4.model.BackboneElement tgt, String... extensionUrlsToIgnore) throws FHIRException {
ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt, extensionUrlsToIgnore);
for (org.hl7.fhir.r5.model.Extension e : src.getModifierExtension()) {
tgt.addModifierExtension(Extension40_50.convertExtension(e));
if (!isExemptExtension(e.getUrl(), extensionUrlsToIgnore)) {
tgt.addModifierExtension(Extension40_50.convertExtension(e));
}
}
}

View File

@ -207,6 +207,9 @@ public class ConceptMap40_50 {
for (org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent t : src.getTarget()) {
if (t.getEquivalence() == org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence.UNMATCHED) {
tgt.setNoMap(true);
if (t.hasComment()) {
tgt.addExtension("http://hl7.org/fhir/4.0/StructureDefinition/extension-ConceptMap.group.element.target.comment", ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().convertType(t.getCommentElement()));
}
} else {
tgt.addTarget(convertTargetElementComponent(t, tgtMap));
}
@ -218,13 +221,18 @@ public class ConceptMap40_50 {
if (src == null)
return null;
org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent tgt = new org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent();
ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyBackboneElement(src, tgt);
ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyBackboneElement(src, tgt, "http://hl7.org/fhir/4.0/StructureDefinition/extension-ConceptMap.group.element.target.comment");
if (src.hasCode())
tgt.setCodeElement(Code40_50.convertCode(src.getCodeElement()));
if (src.hasDisplay())
tgt.setDisplayElement(String40_50.convertString(src.getDisplayElement()));
if (src.hasNoMap() && src.getNoMap() == true) {
tgt.addTarget(new org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent().setEquivalence(org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence.UNMATCHED));
org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent t = new org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent();
t.setEquivalence(org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence.UNMATCHED);
if (src.hasExtension("http://hl7.org/fhir/4.0/StructureDefinition/extension-ConceptMap.group.element.target.comment")) {
t.setCommentElement((org.hl7.fhir.r4.model.StringType) ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().convertType(src.getExtensionByUrl("http://hl7.org/fhir/4.0/StructureDefinition/extension-ConceptMap.group.element.target.comment").getValue()));
}
tgt.addTarget(t);
} else {
for (org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent t : src.getTarget())
tgt.addTarget(convertTargetElementComponent(t, srcMap));

View File

@ -18,6 +18,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.formats.JsonParser;
import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
@ -60,8 +61,18 @@ public class VSACImporter extends OIDBasedValueSetImporter {
CapabilityStatement cs = fhirToolingClient.getCapabilitiesStatement();
JsonParser json = new JsonParser();
json.setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "vsac-capability-statmenet.json")), cs);
json.setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "vsac-capability-statement.json")), cs);
System.out.println("CodeSystems");
CodeSystem css = fhirToolingClient.fetchResource(CodeSystem.class, "CDCNHSN");
json.setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(dest, "CodeSystem-CDCNHSN.json")), css);
css = fhirToolingClient.fetchResource(CodeSystem.class, "CDCREC");
json.setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(dest, "CodeSystem-CDCREC.json")), css);
css = fhirToolingClient.fetchResource(CodeSystem.class, "HSLOC");
json.setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(dest, "CodeSystem-HSLOC.json")), css);
css = fhirToolingClient.fetchResource(CodeSystem.class, "SOP");
json.setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(dest, "CodeSystem-SOP.json")), css);
System.out.println("Loading");
List<String> oids = new ArrayList<>();
while (csv.line()) {

View File

@ -0,0 +1,31 @@
package org.hl7.fhir.convertors.conv40_50;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class ConceptMap40_50Test {
@Test
@DisplayName("Test r5 -> r4 ConceptMap conversion.")
public void testR5_R4() throws IOException {
InputStream r4_input = this.getClass().getResourceAsStream("/cm_nomap.json");
org.hl7.fhir.r4.model.ConceptMap r4_actual = (org.hl7.fhir.r4.model.ConceptMap) new org.hl7.fhir.r4.formats.JsonParser().parse(r4_input);
org.hl7.fhir.r5.model.Resource r5_conv = VersionConvertorFactory_40_50.convertResource(r4_actual);
org.hl7.fhir.r4.model.Resource r4_conv = VersionConvertorFactory_40_50.convertResource(r5_conv);
System.out.println(new org.hl7.fhir.r4.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(r4_actual));
System.out.println(new org.hl7.fhir.r4.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(r4_conv));
assertTrue(r4_actual.equalsDeep(r4_conv), "should be the same");
}
}

View File

@ -0,0 +1,30 @@
{
"resourceType": "ConceptMap",
"id": "appointment-status-concept-map",
"url": "https://hl7.fi/fhir/finnish-scheduling/ConceptMap/appointment-status-concept-map",
"title": "FHIR Appointment status codes and Ajanvaraus - Ajanvarauksen tila",
"description": "Mapping between the Finnish logical model [*Ajanvaraus - Ajanvarauksen tila*](https://koodistopalvelu.kanta.fi/codeserver/pages/classification-view-page.xhtml?classificationKey=1943) (oid `1.2.246.537.6.881`) and FHIR Appoinment status codes, in both directions.",
"status": "draft",
"sourceUri": "https://koodistopalvelu.kanta.fi/codeserver/pages/classification-view-page.xhtml?classificationKey=1943",
"targetCanonical": "http://hl7.org/fhir/appointmentstatus",
"group": [{
"source": "https://koodistopalvelu.kanta.fi/codeserver/pages/classification-view-page.xhtml?classificationKey=1943",
"target": "http://hl7.org/fhir/appointmentstatus",
"element": [{
"code": "5",
"display": "Siirretty",
"target": [{
"equivalence": "unmatched",
"comment": "There is no status for rescheduled appointments in FHIR. This code SHOULD be mapped to cancelled, if required. Note that this code is deprecated."
}]
}, {
"code" : "3",
"display" : "Varattu",
"target" : [{
"code" : "booked",
"display" : "Booked",
"equivalence" : "equal"
}]
}]
]}
}

View File

@ -3658,23 +3658,22 @@ public class FHIRPathEngine {
case LowBoundary:
case HighBoundary: {
checkContextContinuous(focus, exp.getFunction().toCode(), exp);
checkContextContinuous(focus, exp.getFunction().toCode(), exp, true);
if (paramTypes.size() > 0) {
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes,
new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer));
}
if (focus.hasType("decimal")
&& (focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal, TypeDetails.FP_DateTime);
} else if (focus.hasType("decimal")) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal);
if ((focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal, TypeDetails.FP_DateTime);
} else if (focus.hasType("decimal") || focus.hasType("integer")) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal);
} else {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime);
}
}
case Precision: {
checkContextContinuous(focus, exp.getFunction().toCode(), exp);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer);
checkContextContinuous(focus, exp.getFunction().toCode(), exp, false);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer);
}
case Custom: {
@ -3778,9 +3777,8 @@ public class FHIRPathEngine {
}
}
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException {
if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime")
&& !focus.hasType("time") && !focus.hasType("Quantity")) {
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr, boolean allowInteger) throws PathEngineException {
if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time") && !focus.hasType("Quantity") && !(allowInteger && focus.hasType("integer"))) {
throw makeException(expr, I18nConstants.FHIRPATH_CONTINUOUS_ONLY, name, focus.describe());
}
}
@ -4223,42 +4221,43 @@ public class FHIRPathEngine {
if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "lowBoundary", focus.size());
}
int precision = 0;
Integer precision = null;
if (expr.getParameters().size() > 0) {
List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true);
if (n1.size() != 1) {
throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values",
"integer");
throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values", "integer");
}
precision = Integer.parseInt(n1.get(0).primitiveValue());
}
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("decimal")) {
result
.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision)));
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("integer")) {
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("date")) {
result
.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision)));
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == null ? 10 : precision)));
} else if (base.hasType("dateTime")) {
result
.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == null ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == null ? 9 : precision)));
} else if (base.hasType("Quantity")) {
String value = getNamedValue(base, "value");
Base v = base.copy();
v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == null ? 8 : precision)));
result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(),
"decimal or date");
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
}
return result;
}
private List<Base> funcHighBoundary(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
if (focus.size() == 0) {
return makeNull();
@ -4266,41 +4265,43 @@ public class FHIRPathEngine {
if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size());
}
int precision = 0;
Integer precision = null;
if (expr.getParameters().size() > 0) {
List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true);
if (n1.size() != 1) {
throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values",
"integer");
throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values", "integer");
}
precision = Integer.parseInt(n1.get(0).primitiveValue());
}
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("decimal")) {
result.add(
new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision)));
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("integer")) {
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("date")) {
result
.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision)));
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == null ? 10 : precision)));
} else if (base.hasType("dateTime")) {
result
.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == null ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == null ? 9 : precision)));
} else if (base.hasType("Quantity")) {
String value = getNamedValue(base, "value");
Base v = base.copy();
v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == null ? 8 : precision)));
result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(),
"decimal or date");
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
}
return result;
}
private List<Base> funcPrecision(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
if (focus.size() != 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size());

View File

@ -3660,23 +3660,22 @@ public class FHIRPathEngine {
case LowBoundary:
case HighBoundary: {
checkContextContinuous(focus, exp.getFunction().toCode(), exp);
checkContextContinuous(focus, exp.getFunction().toCode(), exp, true);
if (paramTypes.size() > 0) {
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes,
new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer));
}
if (focus.hasType("decimal")
&& (focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal, TypeDetails.FP_DateTime);
} else if (focus.hasType("decimal")) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal);
if ((focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal, TypeDetails.FP_DateTime);
} else if (focus.hasType("decimal") || focus.hasType("integer")) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal);
} else {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime);
}
}
case Precision: {
checkContextContinuous(focus, exp.getFunction().toCode(), exp);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer);
checkContextContinuous(focus, exp.getFunction().toCode(), exp, false);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer);
}
case Custom: {
@ -3780,9 +3779,8 @@ public class FHIRPathEngine {
}
}
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException {
if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime")
&& !focus.hasType("time") && !focus.hasType("Quantity")) {
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr, boolean allowInteger) throws PathEngineException {
if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time") && !focus.hasType("Quantity") && !(allowInteger && focus.hasType("integer"))) {
throw makeException(expr, I18nConstants.FHIRPATH_CONTINUOUS_ONLY, name, focus.describe());
}
}
@ -4233,42 +4231,43 @@ public class FHIRPathEngine {
if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "lowBoundary", focus.size());
}
int precision = 0;
Integer precision = null;
if (expr.getParameters().size() > 0) {
List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true);
if (n1.size() != 1) {
throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values",
"integer");
throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values", "integer");
}
precision = Integer.parseInt(n1.get(0).primitiveValue());
}
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("decimal")) {
result
.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision)));
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("integer")) {
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("date")) {
result
.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision)));
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == null ? 10 : precision)));
} else if (base.hasType("dateTime")) {
result
.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == null ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == null ? 9 : precision)));
} else if (base.hasType("Quantity")) {
String value = getNamedValue(base, "value");
Base v = base.copy();
v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == null ? 8 : precision)));
result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(),
"decimal or date");
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
}
return result;
}
private List<Base> funcHighBoundary(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
if (focus.size() == 0) {
return makeNull();
@ -4276,41 +4275,43 @@ public class FHIRPathEngine {
if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size());
}
int precision = 0;
Integer precision = null;
if (expr.getParameters().size() > 0) {
List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true);
if (n1.size() != 1) {
throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values",
"integer");
throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values", "integer");
}
precision = Integer.parseInt(n1.get(0).primitiveValue());
}
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("decimal")) {
result.add(
new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision)));
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("integer")) {
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("date")) {
result
.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision)));
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == null ? 10 : precision)));
} else if (base.hasType("dateTime")) {
result
.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == null ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == null ? 9 : precision)));
} else if (base.hasType("Quantity")) {
String value = getNamedValue(base, "value");
Base v = base.copy();
v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == null ? 8 : precision)));
result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(),
"decimal or date");
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
}
return result;
}
private List<Base> funcPrecision(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
if (focus.size() != 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size());

View File

@ -0,0 +1,227 @@
package org.hl7.fhir.r5.conformance.profile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.r5.model.Element;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
public class MappingAssistant {
public enum MappingMergeModeOption {
DUPLICATE, // if there's more than one mapping for the same URI, just keep them all
IGNORE, // if there's more than one, keep the first
OVERWRITE, // if there's opre than, keep the last
APPEND, // if there's more than one, append them with ';'
}
private MappingMergeModeOption mappingMergeMode = MappingMergeModeOption.APPEND;
private StructureDefinition base;
private StructureDefinition derived;
private List<StructureDefinitionMappingComponent> masterList= new ArrayList<StructureDefinition.StructureDefinitionMappingComponent>();
private Map<String, String> renames = new HashMap<>();
private String version;
public MappingAssistant(MappingMergeModeOption mappingMergeMode, StructureDefinition base, StructureDefinition derived, String version) {
this.mappingMergeMode = mappingMergeMode;
this.base = base;
this.derived = derived;
this.version = version;
// figure out where we're going to be:
// mappings declared in derived get priority; we do not change them either
for (StructureDefinitionMappingComponent m : derived.getMapping()) {
masterList.add(m);
if (!isSuppressed(m)) {
m.setUserData("private-marked-as-derived", true);
}
}
// now, look at the base profile. If mappings in there match one in the derived, then we use that, otherwise, we add it to the list
for (StructureDefinitionMappingComponent m : base.getMapping()) {
StructureDefinitionMappingComponent md = findMatchInDerived(m);
if (md == null) {
if (nameExists(m.getIdentity())) {
int i = 1;
String n = m.getIdentity() + i;
while (nameExists(n)) {
i++;
n = m.getIdentity() + i;
}
renames.put(m.getIdentity(), n);
masterList.add(m.copy().setName(n));
} else {
masterList.add(m.copy());
}
} else {
if (!md.hasName() && m.hasName()) {
md.setName(m.getName());
}
if (!md.hasUri() && m.hasUri()) {
md.setUri(m.getUri());
}
if (!md.hasComment() && m.hasComment()) {
md.setComment(m.getComment());
}
if (!m.getIdentity().equals(md.getIdentity())) {
renames.put(m.getIdentity(), md.getIdentity());
}
}
}
}
private boolean nameExists(String n) {
for (StructureDefinitionMappingComponent md : masterList) {
if (n.equals(md.getIdentity())) {
return true;
}
}
return false;
}
private StructureDefinitionMappingComponent findMatchInDerived(StructureDefinitionMappingComponent m) {
for (StructureDefinitionMappingComponent md : derived.getMapping()) {
// if the URIs match, they match, irregardless of anything else
if (md.hasUri() && m.hasUri() && md.getUri().equals(m.getUri())) {
return md;
}
// if the codes match
if (md.hasIdentity() && m.hasIdentity() && md.getIdentity().equals(m.getIdentity())) {
// the names have to match if present
if (!md.hasName() || !m.hasName() || md.getName().equals(m.getName())) {
return md;
}
}
}
return null;
}
public void update() {
Set<StructureDefinitionMappingComponent> usedList= new HashSet<StructureDefinition.StructureDefinitionMappingComponent>();
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
for (ElementDefinitionMappingComponent m : ed.getMapping()) {
StructureDefinitionMappingComponent def = findDefinition(m.getIdentity());
if (def != null) {
usedList.add(def);
} else {
// not sure what to do?
}
}
}
derived.getMapping().clear();
for (StructureDefinitionMappingComponent t : masterList) {
if (usedList.contains(t) || t.hasUserData("private-marked-as-derived")) {
derived.getMapping().add(t);
}
}
}
public void merge(ElementDefinition base, ElementDefinition derived) {
List<ElementDefinitionMappingComponent> list = new ArrayList<>();
addMappings(list, base.getMapping(), renames);
if (derived.hasMapping()) {
addMappings(list, derived.getMapping(), null);
}
derived.setMapping(list);
// trim anything
for (ElementDefinitionMappingComponent m : base.getMapping()) {
if (m.hasMap()) {
m.setMap(m.getMap().trim());
}
}
}
private void addMappings(List<ElementDefinitionMappingComponent> destination, List<ElementDefinitionMappingComponent> source, Map<String, String> renames2) {
for (ElementDefinitionMappingComponent s : source) {
if (!isSuppressed(s)) {
String name = s.getIdentity();
if (!isSuppressed(name)) {
if (renames2 != null && renames2.containsKey(name)) {
name = renames2.get(name);
}
boolean found = false;
for (ElementDefinitionMappingComponent d : destination) {
if (compareMaps(name, s, d)) {
found = true;
d.setUserData(ProfileUtilities.UD_DERIVATION_EQUALS, true);
break;
}
}
if (!found) {
destination.add(s.setIdentity(name));
}
}
}
}
}
private boolean isSuppressed(String name) {
StructureDefinitionMappingComponent m = findDefinition(name);
return m != null && isSuppressed(m);
}
private boolean isSuppressed(Element s) {
return ToolingExtensions.readBoolExtension(s, ToolingExtensions.EXT_SUPPRESSED);
}
private StructureDefinitionMappingComponent findDefinition(String name) {
for (StructureDefinitionMappingComponent t : masterList) {
if (t.getIdentity().equals(name)) {
return t;
}
}
return null;
}
private boolean compareMaps(String name, ElementDefinitionMappingComponent s, ElementDefinitionMappingComponent d) {
if (d.getIdentity().equals(name) && d.getMap().equals(s.getMap())) {
return true;
}
if (VersionUtilities.isR5Plus(version)) {
if (d.getIdentity().equals(name)) {
switch (mappingMergeMode) {
case APPEND:
if (!Utilities.splitStrings(d.getMap(), "\\,").contains(s.getMap())) {
d.setMap(d.getMap()+","+s.getMap());
}
return true;
case DUPLICATE:
return false;
case IGNORE:
d.setMap(s.getMap());
return true;
case OVERWRITE:
return true;
default:
return false;
}
} else {
return false;
}
} else {
return false;
}
}
}

View File

@ -128,7 +128,7 @@ public class ProfilePathProcessor {
}
protected static void processPaths(ProfileUtilities profileUtilities, StructureDefinition base, StructureDefinition derived, String url, String webUrl, StructureDefinition.StructureDefinitionDifferentialComponent differential, StructureDefinition.StructureDefinitionSnapshotComponent baseSnapshot) {
protected static void processPaths(ProfileUtilities profileUtilities, StructureDefinition base, StructureDefinition derived, String url, String webUrl, StructureDefinition.StructureDefinitionDifferentialComponent differential, StructureDefinition.StructureDefinitionSnapshotComponent baseSnapshot, MappingAssistant mapHelper) {
ProfilePathProcessorState cursors = new ProfilePathProcessorState(
baseSnapshot,
@ -152,16 +152,17 @@ public class ProfilePathProcessor {
.withRedirector(new ArrayList<ElementRedirection>())
.withSourceStructureDefinition(base)
.withDerived(derived)
.withSlicing(new PathSlicingParams()).processPaths(cursors);
.withSlicing(new PathSlicingParams()).processPaths(cursors, mapHelper);
}
/**
* @param cursors
* @param mapHelper
* @throws DefinitionException, FHIRException
* @throws Exception
*/
private ElementDefinition processPaths(final ProfilePathProcessorState cursors) throws FHIRException {
private ElementDefinition processPaths(final ProfilePathProcessorState cursors, MappingAssistant mapHelper) throws FHIRException {
debugProcessPathsEntry(cursors);
ElementDefinition res = null;
List<TypeSlice> typeList = new ArrayList<>();
@ -177,13 +178,13 @@ public class ProfilePathProcessor {
// in the simple case, source is not sliced.
if (!currentBase.hasSlicing() || currentBasePath.equals(getSlicing().getPath()))
{
ElementDefinition currentRes = processSimplePath(currentBase, currentBasePath, diffMatches, typeList, cursors);
ElementDefinition currentRes = processSimplePath(currentBase, currentBasePath, diffMatches, typeList, cursors, mapHelper);
if (res == null) {
res = currentRes;
}
}
else {
processPathWithSlicedBase(currentBase, currentBasePath, diffMatches, typeList, cursors);
processPathWithSlicedBase(currentBase, currentBasePath, diffMatches, typeList, cursors, mapHelper);
}
}
@ -238,26 +239,26 @@ public class ProfilePathProcessor {
final String currentBasePath,
final List<ElementDefinition> diffMatches,
final List<TypeSlice> typeList,
final ProfilePathProcessorState cursors) throws FHIRException {
final ProfilePathProcessorState cursors, MappingAssistant mapHelper) throws FHIRException {
ElementDefinition res = null;
// the differential doesn't say anything about this item
// so we just copy it in
if (diffMatches.isEmpty())
processSimplePathWithEmptyDiffMatches(currentBase, currentBasePath, diffMatches, cursors);
processSimplePathWithEmptyDiffMatches(currentBase, currentBasePath, diffMatches, cursors, mapHelper);
// one matching element in the differential
else if (oneMatchingElementInDifferential(getSlicing().isDone(), currentBasePath, diffMatches))
res = processSimplePathWithOneMatchingElementInDifferential(currentBase, currentBasePath, diffMatches, cursors);
res = processSimplePathWithOneMatchingElementInDifferential(currentBase, currentBasePath, diffMatches, cursors, mapHelper);
else if (profileUtilities.diffsConstrainTypes(diffMatches, currentBasePath, typeList))
processSimplePathWhereDiffsConstrainTypes(currentBasePath, diffMatches, typeList, cursors);
processSimplePathWhereDiffsConstrainTypes(currentBasePath, diffMatches, typeList, cursors, mapHelper);
else
processSimplePathDefault(currentBase, currentBasePath, diffMatches, cursors);
processSimplePathDefault(currentBase, currentBasePath, diffMatches, cursors, mapHelper);
return res;
}
private void processSimplePathDefault(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors) {
private void processSimplePathDefault(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, MappingAssistant mapHelper) {
// ok, the differential slices the item. Let's check our pre-conditions to ensure that this is correct
if (!profileUtilities.unbounded(currentBase) && !profileUtilities.isSlicedToOneOnly(diffMatches.get(0)))
// you can only slice an element that doesn't repeat if the sum total of your slices is limited to 1
@ -280,7 +281,7 @@ public class ProfilePathProcessor {
.withBaseLimit(newBaseLimit)
.withDiffLimit(newDiffLimit)
.withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0)).withSlicing(new PathSlicingParams(true, null, null))
.processPaths(new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase));
.processPaths(new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase), mapHelper);
if (e == null)
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.DID_NOT_FIND_SINGLE_SLICE_, diffMatches.get(0).getPath()));
e.setSlicing(diffMatches.get(0).getSlicing());
@ -322,7 +323,7 @@ public class ProfilePathProcessor {
// differential - if the first one in the list has a name, we'll process it. Else we'll treat it as the base definition of the slice.
if (!diffMatches.get(0).hasSliceName()) {
profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), isTrimDifferential(), getUrl(),getSourceStructureDefinition(), getDerived(), diffPath(diffMatches.get(0)));
profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), isTrimDifferential(), getUrl(),getSourceStructureDefinition(), getDerived(), diffPath(diffMatches.get(0)), mapHelper);
profileUtilities.removeStatusExtensions(outcome);
if (!outcome.hasContentReference() && !outcome.hasType() && outcome.getPath().contains(".")) {
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.NOT_DONE_YET));
@ -350,7 +351,7 @@ public class ProfilePathProcessor {
.withContextPathSource(currentBasePath)
.withContextPathTarget(outcome.getPath()).withSlicing(new PathSlicingParams()) /* starting again on the data type, but skip the root */
. processPaths(new ProfilePathProcessorState(dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
}
}
start++;
@ -375,7 +376,7 @@ public class ProfilePathProcessor {
.withDiffLimit(newDiffLimit)
.withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, i))
.withSlicing(new PathSlicingParams(true, slicerElement, null))
.processPaths(new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase));
.processPaths(new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase), mapHelper);
}
// ok, done with that - next in the base list
cursors.baseCursor = newBaseLimit + 1;
@ -400,7 +401,7 @@ public class ProfilePathProcessor {
return Base.compareDeep(s1.getDiscriminator(), s2.getDiscriminator(), false);
}
private void processSimplePathWhereDiffsConstrainTypes(String currentBasePath, List<ElementDefinition> diffMatches, List<TypeSlice> typeList, ProfilePathProcessorState cursors) {
private void processSimplePathWhereDiffsConstrainTypes(String currentBasePath, List<ElementDefinition> diffMatches, List<TypeSlice> typeList, ProfilePathProcessorState cursors, MappingAssistant mapHelper) {
int start = 0;
int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor);
int newDiffCursor = getDifferential().getElement().indexOf(diffMatches.get(0));
@ -498,7 +499,7 @@ public class ProfilePathProcessor {
.withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0))
.withSlicing(new PathSlicingParams(true, null, null))
.processPaths(new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
if (elementDefinition == null)
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.DID_NOT_FIND_TYPE_ROOT_, path));
// now set up slicing on the e (cause it was wiped by what we called.
@ -531,7 +532,7 @@ public class ProfilePathProcessor {
.withDiffLimit(newDiffLimit)
.withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, i))
.withSlicing(new PathSlicingParams(true, elementDefinition, null))
.processPaths(new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase));
.processPaths(new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase), mapHelper);
if (typeList.size() > start + 1) {
typeSliceElement.setMin(0);
}
@ -576,7 +577,7 @@ public class ProfilePathProcessor {
cursors.diffCursor = newDiffLimit + 1;
}
private ElementDefinition processSimplePathWithOneMatchingElementInDifferential(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors) {
private ElementDefinition processSimplePathWithOneMatchingElementInDifferential(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, MappingAssistant mapHelper) {
ElementDefinition res;
ElementDefinition template = null;
if (diffMatches.get(0).hasType() && "Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode()) && !profileUtilities.isValidType(diffMatches.get(0).getType().get(0), currentBase)) {
@ -684,7 +685,7 @@ public class ProfilePathProcessor {
}
}
}
profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), isTrimDifferential(), getUrl(), getSourceStructureDefinition(), getDerived(), diffPath(diffMatches.get(0)));
profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), isTrimDifferential(), getUrl(), getSourceStructureDefinition(), getDerived(), diffPath(diffMatches.get(0)), mapHelper);
profileUtilities.removeStatusExtensions(outcome);
// if (outcome.getPath().endsWith("[x]") && outcome.getType().size() == 1 && !outcome.getType().get(0).getCode().equals("*") && !diffMatches.get(0).hasSlicing()) // if the base profile allows multiple types, but the profile only allows one, rename it
// outcome.setPath(outcome.getPath().substring(0, outcome.getPath().length()-3)+Utilities.capitalize(outcome.getType().get(0).getCode()));
@ -746,7 +747,7 @@ public class ProfilePathProcessor {
.withContextPathSource(target.getElement().getPath())
.withContextPathTarget(diffMatches.get(0).getPath()).withRedirector(profileUtilities.redirectorStack(getRedirector(), outcome, currentBasePath))
.withSourceStructureDefinition(target.getSource())
.withSlicing(new PathSlicingParams()).processPaths(new ProfilePathProcessorState(cursors.base, newBaseCursor, start - 1, cursors.contextName, cursors.resultPathBase));
.withSlicing(new PathSlicingParams()).processPaths(new ProfilePathProcessorState(cursors.base, newBaseCursor, start - 1, cursors.contextName, cursors.resultPathBase), mapHelper);
} else {
final int newBaseCursor = cursors.base.getElement().indexOf(target.getElement()) + 1;
int newBaseLimit = newBaseCursor;
@ -761,7 +762,7 @@ public class ProfilePathProcessor {
.withContextPathTarget(diffMatches.get(0).getPath())
.withRedirector(profileUtilities.redirectorStack(getRedirector(), outcome, currentBasePath))
.withSlicing(new PathSlicingParams()).processPaths(
new ProfilePathProcessorState(cursors.base, newBaseCursor, start - 1, cursors.contextName, cursors.resultPathBase));
new ProfilePathProcessorState(cursors.base, newBaseCursor, start - 1, cursors.contextName, cursors.resultPathBase), mapHelper);
}
} else {
StructureDefinition dt = outcome.getType().size() == 1 ? profileUtilities.getProfileForDataType(outcome.getType().get(0), getWebUrl(), getDerived()) : profileUtilities.getProfileForDataType("Element");
@ -778,7 +779,7 @@ public class ProfilePathProcessor {
.withContextPathSource(diffMatches.get(0).getPath()).withContextPathTarget(outcome.getPath()).withRedirector(new ArrayList<ElementRedirection>())
.withSlicing(new PathSlicingParams()). /* starting again on the data type, but skip the root */
processPaths(new ProfilePathProcessorState(dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
}
}
}
@ -864,7 +865,7 @@ public class ProfilePathProcessor {
}
private void processSimplePathWithEmptyDiffMatches(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors) {
private void processSimplePathWithEmptyDiffMatches(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, MappingAssistant mapHelper) {
ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy());
outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource()));
profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl());
@ -884,7 +885,7 @@ public class ProfilePathProcessor {
// did we implicitly step into a new type?
if (baseHasChildren(cursors.base, currentBase)) { // not a new type here
this.incrementDebugIndent().withSlicing(new PathSlicingParams()). processPaths( new ProfilePathProcessorState(cursors.base, cursors.baseCursor + 1, cursors.diffCursor, cursors.contextName, cursors.resultPathBase));
this.incrementDebugIndent().withSlicing(new PathSlicingParams()). processPaths( new ProfilePathProcessorState(cursors.base, cursors.baseCursor + 1, cursors.diffCursor, cursors.contextName, cursors.resultPathBase), mapHelper);
cursors.baseCursor = indexOfFirstNonChild(cursors.base, currentBase, cursors.baseCursor + 1, getBaseLimit());
}
else {
@ -933,7 +934,7 @@ public class ProfilePathProcessor {
.withRedirector(profileUtilities.redirectorStack(getRedirector(), outcome, currentBasePath))
.withSourceStructureDefinition(tgt.getSource())
.withSlicing(new PathSlicingParams()).processPaths(
new ProfilePathProcessorState(cursors.base, newBaseCursor, start - 1, cursors.contextName, cursors.resultPathBase));
new ProfilePathProcessorState(cursors.base, newBaseCursor, start - 1, cursors.contextName, cursors.resultPathBase), mapHelper);
} else {
int newBaseCursor = cursors.base.getElement().indexOf(tgt.getElement()) + 1;
int newBaseLimit = newBaseCursor;
@ -948,7 +949,7 @@ public class ProfilePathProcessor {
.withContextPathSource(tgt.getElement().getPath())
.withContextPathTarget(outcome.getPath())
.withRedirector(profileUtilities.redirectorStack(getRedirector(), outcome, currentBasePath)).withSlicing(new PathSlicingParams()).processPaths(
new ProfilePathProcessorState(cursors.base, newBaseCursor, start, cursors.contextName, cursors.resultPathBase));
new ProfilePathProcessorState(cursors.base, newBaseCursor, start, cursors.contextName, cursors.resultPathBase), mapHelper);
}
} else {
StructureDefinition dt = outcome.getType().size() > 1 ? profileUtilities.getContext().fetchTypeDefinition("Element") : profileUtilities.getProfileForDataType(outcome.getType().get(0), getWebUrl(), getDerived());
@ -967,7 +968,7 @@ public class ProfilePathProcessor {
.withContextPathTarget(outcome.getPath())
.withSlicing(new PathSlicingParams()).processPaths( /* starting again on the data type, but skip the root */
new ProfilePathProcessorState(dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
} else {
this
@ -979,7 +980,7 @@ public class ProfilePathProcessor {
.withContextPathTarget( outcome.getPath())
.withRedirector(profileUtilities.redirectorStack(getRedirector(), currentBase, currentBasePath)).withSlicing(new PathSlicingParams()).processPaths( /* starting again on the data type, but skip the root */
new ProfilePathProcessorState(dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
}
}
}
@ -991,7 +992,7 @@ public class ProfilePathProcessor {
ElementDefinition currentBase,
String currentBasePath,
List<ElementDefinition> diffMatches, List<TypeSlice> typeList,
final ProfilePathProcessorState cursors
final ProfilePathProcessorState cursors, MappingAssistant mapHelper
) {
// the item is already sliced in the base profile.
// here's the rules
@ -1004,19 +1005,19 @@ public class ProfilePathProcessor {
String path = currentBase.getPath();
if (diffMatches.isEmpty()) {
processPathWithSlicedBaseAndEmptyDiffMatches(currentBase, currentBasePath, diffMatches, cursors, path);
processPathWithSlicedBaseAndEmptyDiffMatches(currentBase, currentBasePath, diffMatches, cursors, path, mapHelper);
}
else if (profileUtilities.diffsConstrainTypes(diffMatches, currentBasePath, typeList))
{
processPathWithSlicedBaseWhereDiffsConstrainTypes(currentBasePath, diffMatches, typeList, cursors);
processPathWithSlicedBaseWhereDiffsConstrainTypes(currentBasePath, diffMatches, typeList, cursors, mapHelper);
}
else
{
processPathWithSlicedBaseDefault(currentBase, currentBasePath, diffMatches, cursors, path);
processPathWithSlicedBaseDefault(currentBase, currentBasePath, diffMatches, cursors, path, mapHelper);
}
}
private void processPathWithSlicedBaseDefault(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, String path) {
private void processPathWithSlicedBaseDefault(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, String path, MappingAssistant mapHelper) {
// first - check that the slicing is ok
boolean closed = currentBase.getSlicing().getRules() == ElementDefinition.SlicingRules.CLOSED;
int diffpos = 0;
@ -1037,7 +1038,7 @@ public class ProfilePathProcessor {
profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl());
if (diffMatches.get(0).hasSlicing() || !diffMatches.get(0).hasSliceName()) {
profileUtilities.updateFromSlicing(outcome.getSlicing(), diffMatches.get(0).getSlicing());
profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), closed, getUrl(), getSourceStructureDefinition(), getDerived(), diffPath(diffMatches.get(0))); // if there's no slice, we don't want to update the unsliced description
profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), closed, getUrl(), getSourceStructureDefinition(), getDerived(), diffPath(diffMatches.get(0)), mapHelper); // if there's no slice, we don't want to update the unsliced description
profileUtilities.removeStatusExtensions(outcome);
} else if (!diffMatches.get(0).hasSliceName()) {
diffMatches.get(0).setUserData(profileUtilities.UD_GENERATED_IN_SNAPSHOT, outcome); // because of updateFromDefinition isn't called
@ -1076,7 +1077,7 @@ public class ProfilePathProcessor {
.withContextPathSource(currentBasePath).withContextPathTarget(outcome.getPath())
.withSlicing(new PathSlicingParams()).processPaths(
new ProfilePathProcessorState(dt.getSnapshot(), 1, newDiffCursor,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
} else {
this
@ -1086,7 +1087,7 @@ public class ProfilePathProcessor {
.withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0))
.withRedirector(null).withSlicing(new PathSlicingParams()).processPaths(
new ProfilePathProcessorState(cursors.base, cursors.baseCursor + 1, newDiffCursor,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
}
// throw new Error("Not done yet");
// } else if (currentBase.getType().get(0).getCode().equals("BackboneElement") && diffMatches.size() > 0 && diffMatches.get(0).hasSliceName()) {
@ -1127,7 +1128,7 @@ public class ProfilePathProcessor {
.withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, diffpos))
.withTrimDifferential(closed)
.withSlicing(new PathSlicingParams(true, null, null)).processPaths(
new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase));
new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase), mapHelper);
// ok, done with that - now set the cursors for if this is the end
cursors.baseCursor = newBaseLimit;
cursors.diffCursor = newDiffLimit + 1;
@ -1175,7 +1176,7 @@ public class ProfilePathProcessor {
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH));
debugCheck(outcome);
getResult().getElement().add(outcome);
profileUtilities.updateFromDefinition(outcome, diffItem, getProfileName(), isTrimDifferential(), getUrl(), getSourceStructureDefinition(), getDerived(), diffPath(diffItem));
profileUtilities.updateFromDefinition(outcome, diffItem, getProfileName(), isTrimDifferential(), getUrl(), getSourceStructureDefinition(), getDerived(), diffPath(diffItem), mapHelper);
profileUtilities.removeStatusExtensions(outcome);
// --- LM Added this
cursors.diffCursor = getDifferential().getElement().indexOf(diffItem) + 1;
@ -1204,7 +1205,7 @@ public class ProfilePathProcessor {
.withContextPathTarget(cursors.base.getElement().get(0).getPath())
.withSlicing(new PathSlicingParams()).processPaths(
new ProfilePathProcessorState(cursors.base, baseStart, start - 1,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
} else {
StructureDefinition dt = profileUtilities.getProfileForDataType(outcome.getType().get(0), getWebUrl(), getDerived());
// if (t.getCode().equals("Extension") && t.hasProfile() && !t.getProfile().contains(":")) {
@ -1225,7 +1226,7 @@ public class ProfilePathProcessor {
.withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0))
.withContextPathSource(diffMatches.get(0).getPath()).withContextPathTarget(outcome.getPath()).withSlicing(new PathSlicingParams()).processPaths( /* starting again on the data type, but skip the root */
new ProfilePathProcessorState(dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start - 1,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
}
}
}
@ -1243,7 +1244,7 @@ public class ProfilePathProcessor {
}
}
private void processPathWithSlicedBaseWhereDiffsConstrainTypes(String currentBasePath, List<ElementDefinition> diffMatches, List<TypeSlice> typeList, ProfilePathProcessorState cursors) {
private void processPathWithSlicedBaseWhereDiffsConstrainTypes(String currentBasePath, List<ElementDefinition> diffMatches, List<TypeSlice> typeList, ProfilePathProcessorState cursors, MappingAssistant mapHelper) {
int start = 0;
int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor);
int newDiffCursor = getDifferential().getElement().indexOf(diffMatches.get(0));
@ -1329,7 +1330,7 @@ public class ProfilePathProcessor {
.withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches,0))
.withSlicing(new PathSlicingParams(true, null, currentBasePath)).processPaths(
new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
if (e == null)
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.DID_NOT_FIND_TYPE_ROOT_, diffMatches.get(0).getPath()));
// now set up slicing on the e (cause it was wiped by what we called.
@ -1369,7 +1370,7 @@ public class ProfilePathProcessor {
.withDiffLimit(newDiffLimit)
.withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, i))
.withSlicing(new PathSlicingParams(true, e, currentBasePath)).processPaths(
new ProfilePathProcessorState(cursors.base, sStart, newDiffCursor, cursors.contextName, cursors.resultPathBase));
new ProfilePathProcessorState(cursors.base, sStart, newDiffCursor, cursors.contextName, cursors.resultPathBase), mapHelper);
}
if (elementToRemove != null) {
getDifferential().getElement().remove(elementToRemove);
@ -1395,7 +1396,7 @@ public class ProfilePathProcessor {
.withBaseLimit(bs.getEnd())
.withDiffLimit(0)
.withProfileName(getProfileName() + profileUtilities.tail(bs.getDefn().getPath())).withSlicing(new PathSlicingParams(true, e, currentBasePath)).processPaths(
new ProfilePathProcessorState(cursors.base, bs.getStart(), 0, cursors.contextName, cursors.resultPathBase));
new ProfilePathProcessorState(cursors.base, bs.getStart(), 0, cursors.contextName, cursors.resultPathBase), mapHelper);
}
}
@ -1405,7 +1406,7 @@ public class ProfilePathProcessor {
//throw new Error("not done yet - slicing / types @ "+cpath);
}
private void processPathWithSlicedBaseAndEmptyDiffMatches(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, String path) {
private void processPathWithSlicedBaseAndEmptyDiffMatches(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, String path, MappingAssistant mapHelper) {
if (profileUtilities.hasInnerDiffMatches(getDifferential(), path, cursors.diffCursor, getDiffLimit(), cursors.base.getElement(), true)) {
// so we just copy it in
ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy());
@ -1425,7 +1426,7 @@ public class ProfilePathProcessor {
this
.incrementDebugIndent()
.withSlicing(new PathSlicingParams()).processPaths(
new ProfilePathProcessorState(cursors.base, cursors.baseCursor + 1, cursors.diffCursor, cursors.contextName, cursors.resultPathBase));
new ProfilePathProcessorState(cursors.base, cursors.baseCursor + 1, cursors.diffCursor, cursors.contextName, cursors.resultPathBase), mapHelper);
cursors.baseCursor = indexOfFirstNonChild(cursors.base, currentBase, cursors.baseCursor, getBaseLimit());
} else {
StructureDefinition dt = profileUtilities.getTypeForElement(getDifferential(), cursors.diffCursor, getProfileName(), diffMatches, outcome, getWebUrl(), getDerived());
@ -1447,7 +1448,7 @@ public class ProfilePathProcessor {
.withContextPathSource(currentBasePath)
.withContextPathTarget(outcome.getPath()).withSlicing(new PathSlicingParams()).processPaths( /* starting again on the data type, but skip the root */
new ProfilePathProcessorState(dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start,
cursors.contextName, cursors.resultPathBase));
cursors.contextName, cursors.resultPathBase), mapHelper);
}
}
cursors.baseCursor++;

View File

@ -48,6 +48,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.conformance.ElementRedirection;
import org.hl7.fhir.r5.conformance.profile.MappingAssistant.MappingMergeModeOption;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.AllowUnknownProfile;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ElementDefinitionCounter;
import org.hl7.fhir.r5.context.IWorkerContext;
@ -205,13 +206,6 @@ public class ProfileUtilities {
}
public enum MappingMergeModeOption {
DUPLICATE, // if there's more than one mapping for the same URI, just keep them all
IGNORE, // if there's more than one, keep the first
OVERWRITE, // if there's opre than, keep the last
APPEND, // if there's more than one, append them with ';'
}
public enum AllowUnknownProfile {
NONE, // exception if there's any unknown profiles (the default)
NON_EXTNEIONS, // don't raise an exception except on Extension (because more is going on there
@ -640,26 +634,6 @@ public class ProfileUtilities {
}
}
private void updateMaps(StructureDefinition base, StructureDefinition derived) throws DefinitionException {
if (base == null)
throw new DefinitionException(context.formatMessage(I18nConstants.NO_BASE_PROFILE_PROVIDED));
if (derived == null)
throw new DefinitionException(context.formatMessage(I18nConstants.NO_DERIVED_STRUCTURE_PROVIDED));
for (StructureDefinitionMappingComponent baseMap : base.getMapping()) {
boolean found = false;
for (StructureDefinitionMappingComponent derivedMap : derived.getMapping()) {
if (derivedMap.getUri() != null && derivedMap.getUri().equals(baseMap.getUri())) {
found = true;
break;
}
}
if (!found) {
derived.getMapping().add(baseMap);
}
}
}
/**
* Given a base (snapshot) profile structure, and a differential profile, generate a new snapshot profile
*
@ -752,7 +726,9 @@ public class ProfileUtilities {
// debug = true;
// }
ProfilePathProcessor.processPaths(this, base, derived, url, webUrl, diff, baseSnapshot);
MappingAssistant mappingDetails = new MappingAssistant(mappingMergeMode, base, derived, context.getVersion());
ProfilePathProcessor.processPaths(this, base, derived, url, webUrl, diff, baseSnapshot, mappingDetails);
checkGroupConstraints(derived);
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
@ -761,7 +737,7 @@ public class ProfileUtilities {
if (!e.hasUserData(UD_GENERATED_IN_SNAPSHOT) && e.getPath().contains(".")) {
ElementDefinition existing = getElementInCurrentContext(e.getPath(), derived.getSnapshot().getElement());
if (existing != null) {
updateFromDefinition(existing, e, profileName, false, url, base, derived, "StructureDefinition.differential.element["+i+"]");
updateFromDefinition(existing, e, profileName, false, url, base, derived, "StructureDefinition.differential.element["+i+"]", mappingDetails);
} else {
ElementDefinition outcome = updateURLs(url, webUrl, e.copy());
e.setUserData(UD_GENERATED_IN_SNAPSHOT, outcome);
@ -781,7 +757,7 @@ public class ProfileUtilities {
if (derived.getKind() != StructureDefinitionKind.LOGICAL && !derived.getSnapshot().getElementFirstRep().getType().isEmpty())
throw new Error(context.formatMessage(I18nConstants.TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_, derived.getSnapshot().getElementFirstRep().getPath(), derived.getUrl(), base.getUrl()));
updateMaps(base, derived);
mappingDetails.update();
setIds(derived, false);
if (debug) {
@ -2374,7 +2350,7 @@ public class ProfileUtilities {
}
protected void updateFromDefinition(ElementDefinition dest, ElementDefinition source, String pn, boolean trimDifferential, String purl, StructureDefinition srcSD, StructureDefinition derivedSrc, String path) throws DefinitionException, FHIRException {
protected void updateFromDefinition(ElementDefinition dest, ElementDefinition source, String pn, boolean trimDifferential, String purl, StructureDefinition srcSD, StructureDefinition derivedSrc, String path, MappingAssistant mappings) throws DefinitionException, FHIRException {
source.setUserData(UD_GENERATED_IN_SNAPSHOT, dest);
// we start with a clone of the base profile ('dest') and we copy from the profile ('source')
// over the top for anything the source has
@ -2845,18 +2821,7 @@ public class ProfileUtilities {
t.setUserData(UD_DERIVATION_EQUALS, true);
}
List<ElementDefinitionMappingComponent> list = new ArrayList<>();
list.addAll(base.getMapping());
base.getMapping().clear();
addMappings(base.getMapping(), list);
if (derived.hasMapping()) {
addMappings(base.getMapping(), derived.getMapping());
}
for (ElementDefinitionMappingComponent m : base.getMapping()) {
if (m.hasMap()) {
m.setMap(m.getMap().trim());
}
}
mappings.merge(derived, base); // note reversal of names to be correct in .merge()
// todo: constraints are cumulative. there is no replacing
for (ElementDefinitionConstraintComponent s : base.getConstraint()) {
@ -2960,52 +2925,6 @@ public class ProfileUtilities {
tgt.getExtension().addAll(src.getExtension());
}
private void addMappings(List<ElementDefinitionMappingComponent> destination, List<ElementDefinitionMappingComponent> source) {
for (ElementDefinitionMappingComponent s : source) {
boolean found = false;
for (ElementDefinitionMappingComponent d : destination) {
if (compareMaps(s, d)) {
found = true;
d.setUserData(UD_DERIVATION_EQUALS, true);
break;
}
}
if (!found) {
destination.add(s);
}
}
}
private boolean compareMaps(ElementDefinitionMappingComponent s, ElementDefinitionMappingComponent d) {
if (d.getIdentity().equals(s.getIdentity()) && d.getMap().equals(s.getMap())) {
return true;
}
if (VersionUtilities.isR5Plus(context.getVersion())) {
if (d.getIdentity().equals(s.getIdentity())) {
switch (mappingMergeMode) {
case APPEND:
if (!Utilities.splitStrings(d.getMap(), "\\,").contains(s.getMap())) {
d.setMap(d.getMap()+","+s.getMap());
}
return true;
case DUPLICATE:
return false;
case IGNORE:
d.setMap(s.getMap());
return true;
case OVERWRITE:
return true;
default:
return false;
}
} else {
return false;
}
} else {
return false;
}
}
private void checkTypeDerivation(String purl, StructureDefinition srcSD, ElementDefinition base, ElementDefinition derived, TypeRefComponent ts, String path) {
boolean ok = false;
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();

View File

@ -3735,20 +3735,20 @@ public class FHIRPathEngine {
case LowBoundary:
case HighBoundary: {
checkContextContinuous(focus, exp.getFunction().toCode(), exp);
checkContextContinuous(focus, exp.getFunction().toCode(), exp, true);
if (paramTypes.size() > 0) {
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer));
}
if (focus.hasType("decimal") && (focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) {
if ((focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal, TypeDetails.FP_DateTime);
} else if (focus.hasType("decimal")) {
} else if (focus.hasType("decimal") || focus.hasType("integer")) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal);
} else {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime);
}
}
case Precision: {
checkContextContinuous(focus, exp.getFunction().toCode(), exp);
checkContextContinuous(focus, exp.getFunction().toCode(), exp, false);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer);
}
case hasTemplateIdOf: {
@ -3897,8 +3897,8 @@ public class FHIRPathEngine {
}
}
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException {
if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time") && !focus.hasType("Quantity")) {
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr, boolean allowInteger) throws PathEngineException {
if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time") && !focus.hasType("Quantity") && !(allowInteger && focus.hasType("integer"))) {
throw makeException(expr, I18nConstants.FHIRPATH_CONTINUOUS_ONLY, name, focus.describe());
}
}
@ -4295,7 +4295,7 @@ public class FHIRPathEngine {
if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "lowBoundary", focus.size());
}
int precision = 0;
Integer precision = null;
if (expr.getParameters().size() > 0) {
List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true);
if (n1.size() != 1) {
@ -4308,17 +4308,23 @@ public class FHIRPathEngine {
List<Base> result = new ArrayList<Base>();
if (base.hasType("decimal")) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision)));
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("integer")) {
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("date")) {
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision)));
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == null ? 10 : precision)));
} else if (base.hasType("dateTime")) {
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == null ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == null ? 9 : precision)));
} else if (base.hasType("Quantity")) {
String value = getNamedValue(base, "value");
Base v = base.copy();
v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == null ? 8 : precision)));
result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
@ -4333,7 +4339,7 @@ public class FHIRPathEngine {
if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size());
}
int precision = 0;
Integer precision = null;
if (expr.getParameters().size() > 0) {
List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true);
if (n1.size() != 1) {
@ -4346,17 +4352,23 @@ public class FHIRPathEngine {
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("decimal")) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision)));
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("integer")) {
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("date")) {
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision)));
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == null ? 10 : precision)));
} else if (base.hasType("dateTime")) {
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == null ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == null ? 9 : precision)));
} else if (base.hasType("Quantity")) {
String value = getNamedValue(base, "value");
Base v = base.copy();
v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == null ? 8 : precision)));
result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");

View File

@ -5625,6 +5625,11 @@ public boolean hasTarget() {
@Block()
public static class ElementDefinitionMappingComponent extends Element implements IBaseDatatypeElement {
@Override
public String toString() {
return identity+"=" + map;
}
/**
* An internal reference to the definition of a mapping.
*/

View File

@ -397,6 +397,11 @@ public class StructureDefinition extends CanonicalResource {
@Block()
public static class StructureDefinitionMappingComponent extends BackboneElement implements IBaseBackboneElement {
@Override
public String toString() {
return identity + "=" + uri + " (\""+name+"\")";
}
/**
* An Internal id that is used to identify this mapping set when specific mappings are made.
*/

View File

@ -579,8 +579,8 @@ public class ConceptMapRenderer extends TerminologyRenderer {
if (!ccm.hasRelationship())
tr.td();
else {
if (ccm.getRelationshipElement().hasExtension(ToolingExtensions.EXT_OLD_CONCEPTMAP_EQUIVALENCE)) {
String code = ToolingExtensions.readStringExtension(ccm.getRelationshipElement(), ToolingExtensions.EXT_OLD_CONCEPTMAP_EQUIVALENCE);
if (ccm.hasExtension(ToolingExtensions.EXT_OLD_CONCEPTMAP_EQUIVALENCE)) {
String code = ToolingExtensions.readStringExtension(ccm, ToolingExtensions.EXT_OLD_CONCEPTMAP_EQUIVALENCE);
tr.td().ah(context.prefixLocalHref(eqpath+"#"+code), code).tx(presentEquivalenceCode(code));
} else {
tr.td().ah(context.prefixLocalHref(eqpath+"#"+ccm.getRelationship().toCode()), ccm.getRelationship().toCode()).tx(presentRelationshipCode(ccm.getRelationship().toCode()));

View File

@ -332,13 +332,14 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
}
} else if (!round2 && !exemptFromRendering(child)) {
if (isExtension(p)) {
boolean isExt = isExtension(p);
if (isExt) {
status.setExtensions(true);
}
List<ElementDefinition> grandChildren = getChildrenForPath(profile, allElements, path+"."+p.getName());
filterGrandChildren(grandChildren, path+"."+p.getName(), p);
if (p.getValues().size() > 0) {
if (isSimple(child)) {
if (isSimple(child) && !isExt) {
XhtmlNode para = x.isPara() ? para = x : x.para();
String name = p.getName();
if (name.endsWith("[x]"))
@ -383,22 +384,40 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
x.add(tbl);
}
} else if (isExtension(p)) {
StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, p.getUrl());
for (ResourceWrapper v : p.getValues()) {
if (v != null) {
ResourceWrapper vp = v.child("value");
List<ResourceWrapper> ev = v.children("extension");
if (vp != null) {
XhtmlNode para = x.para();
para.b().addText(labelforExtension(p.getName()));
para.b().addText(labelforExtension(sd, p.getUrl()));
para.tx(": ");
renderLeaf(status, res, vp, profile, child, x, para, false, showCodeDetails, displayHints, path, indent);
} else if (!ev.isEmpty()) {
XhtmlNode bq = x.addTag("blockquote");
bq.para().b().addText(labelforExtension(p.getName()));
XhtmlNode bq = x.addTag("blockquote");
bq.para().b().addText(labelforExtension(sd, p.getUrl()));
// what happens now depends. If all the children are simple extensions, they'll be rendered as properties
boolean allSimple = true;
for (ResourceWrapper vv : ev) {
StructureDefinition ex = context.getWorker().fetchTypeDefinition("Extension");
List<ElementDefinition> children = getChildrenForPath(profile, ex.getSnapshot().getElement(), "Extension");
generateByProfile(status, res, ex, vv, allElements, child, children, bq, "Extension", showCodeDetails, indent+1);
if (!vv.has("value")) {
allSimple = false;
}
}
if (allSimple) {
XhtmlNode ul = bq.ul();
for (ResourceWrapper vv : ev) {
XhtmlNode li = ul.li();
li.tx(labelForSubExtension(vv.primitiveValue("url"), sd));
li.tx(": ");
renderLeaf(status, res, vv.child("value"), sd, child, x, li, isExt, showCodeDetails, displayHints, path, indent);
}
} else {
for (ResourceWrapper vv : ev) {
StructureDefinition ex = context.getWorker().fetchTypeDefinition("Extension");
List<ElementDefinition> children = getChildrenForPath(profile, ex.getSnapshot().getElement(), "Extension");
generateByProfile(status, res, ex, vv, allElements, child, children, bq, "Extension", showCodeDetails, indent+1);
}
}
}
}
@ -417,8 +436,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
private String labelforExtension(String url) {
StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, url);
private String labelForSubExtension(String url, StructureDefinition sd) {
return url;
}
private String labelforExtension(StructureDefinition sd, String url) {
if (sd == null) {
return tail(url);
} else {
@ -467,7 +489,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
public boolean isExtension(NamedResourceWrapperList p) {
return p.getName().contains("extension[");
return p.getUrl() != null;
}
@ -571,12 +593,12 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
// 2. Park it
NamedResourceWrapperList nl = null;
for (NamedResourceWrapperList t : results) {
if (t.getName().equals(url)) {
if (t.getUrl() != null && t.getUrl().equals(url)) {
nl = t;
}
}
if (nl == null) {
nl = new NamedResourceWrapperList(url);
nl = new NamedResourceWrapperList(p.getName(), url);
results.add(nl);
}
nl.getValues().add(v);

View File

@ -578,14 +578,16 @@ public abstract class ResourceRenderer extends DataRenderer {
protected void renderUri(RenderingStatus status, XhtmlNode x, ResourceWrapper uri) throws FHIRFormatError, DefinitionException, IOException {
if (!renderPrimitiveWithNoValue(status, x, uri)) {
String v = uri.primitiveValue();
boolean local = false;
if (context.getContextUtilities().isResource(v)) {
v = "http://hl7.org/fhir/StructureDefinition/"+v;
local = true;
}
if (v.startsWith("mailto:")) {
x.ah(v).addText(v.substring(7));
} else {
ResourceWithReference rr = resolveReference(uri);
ResourceWithReference rr = local ? resolveReference(uri.resource(), v, true) : resolveReference(uri);
if (rr != null) {
if (rr.getResource() == null) {
x.ah(context.prefixLocalHref(rr.getWebPath())).addText(rr.getUrlReference());

View File

@ -31,16 +31,28 @@ public abstract class ResourceWrapper {
public static class NamedResourceWrapperList {
private String name;
private String url; // for extension definitions
private List<ResourceWrapper> values = new ArrayList<ResourceWrapper>();
public NamedResourceWrapperList(String name) {
super();
this.name = name;
}
public NamedResourceWrapperList(String name, String url) {
super();
this.name = name;
this.url = url;
}
public String getName() {
return name;
}
public String getUrl() {
return url;
}
public List<ResourceWrapper> getValues() {
return values;
}

View File

@ -275,6 +275,7 @@ public class ToolingExtensions {
public static final String EXT_VS_CS_SUPPL_NEEDED = "http://hl7.org/fhir/StructureDefinition/valueset-supplement";
public static final String EXT_TYPE_PARAMETER = "http://hl7.org/fhir/tools/StructureDefinition/type-parameter";
public static final String EXT_ALTERNATE_CANONICAL = "http://hl7.org/fhir/StructureDefinition/alternate-canonical";
public static final String EXT_SUPPRESSED = "http://hl7.org/fhir/StructureDefinition/elementdefinition-suppress";
// specific extension helpers

View File

@ -31,6 +31,7 @@ import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r5.model.Narrative;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
@ -582,6 +583,9 @@ public class SnapShotGenerationTests {
rc.setProfileUtilities(new ProfileUtilities(TestingUtilities.getSharedWorkerContext(), null, new TestPKP()));
RendererFactory.factory(output, rc).renderResource(ResourceWrapper.forResource(rc.getContextUtilities(), output));
}
// we just generated it - but we don't care what it is here, just that there's no exceptions (though we need it for the rules)
Narrative txt = output.getText();
output.setText(null);
if (!fail) {
test.output = output;
TestingUtilities.getSharedWorkerContext().cacheResource(output);
@ -603,6 +607,7 @@ public class SnapShotGenerationTests {
}
Assertions.assertTrue(structureDefinitionEquality, "Output does not match expected");
}
output.setText(txt);
}
private StructureDefinition getSD(String url, SnapShotGenerationTestsContext context) throws DefinitionException, FHIRException, IOException {

View File

@ -1695,31 +1695,47 @@ public class Utilities {
value = value.substring(0, value.indexOf("e"));
}
if (isZero(value)) {
return applyPrecision("-0.5000000000000000000000000", precision);
return applyPrecision("-0.5000000000000000000000000", precision, true);
} else if (value.startsWith("-")) {
return "-"+highBoundaryForDecimal(value.substring(1), precision)+(e == null ? "" : e);
} else {
if (value.contains(".")) {
return applyPrecision(minusOne(value)+"50000000000000000000000000000", precision)+(e == null ? "" : e);
return applyPrecision(minusOne(value)+"50000000000000000000000000000", precision, true)+(e == null ? "" : e);
} else {
return applyPrecision(minusOne(value)+".50000000000000000000000000000", precision)+(e == null ? "" : e);
return applyPrecision(minusOne(value)+".50000000000000000000000000000", precision, true)+(e == null ? "" : e);
}
}
}
private static String applyPrecision(String v, int p) {
private static String applyPrecision(String v, int p, boolean down) {
String nv = v;
int dp = -1;
if (nv.contains(".")) {
dp = nv.indexOf(".");
nv = nv.substring(0, dp)+nv.substring(dp+1);
}
String s = null;
int d = p - getDecimalPrecision(v);
if (d == 0) {
return v;
s = nv;
} else if (d > 0) {
return v + padLeft("", '0', d);
s = nv + padLeft("", '0', d);
} else {
if (v.charAt(v.length()+d) >= '6') {
return v.substring(0, v.length()+d-1)+((char) (v.charAt(v.length()+d)+1));
int l = v.length();
int ld = l+d;
if (dp > -1) {
ld--;
}
if (nv.charAt(ld) >= '5' && !down) {
s = nv.substring(0, ld-1)+((char) (nv.charAt(ld-1)+1));
} else {
return v.substring(0, v.length()+d);
s = nv.substring(0, ld);
}
}
if (s.endsWith(".")) {
s = s.substring(0, s.length()-1);
}
return dp == -1 || dp >= s.length() ? s : s.substring(0, dp)+"."+s.substring(dp);
}
private static String minusOne(String value) {
@ -1831,14 +1847,14 @@ public class Utilities {
value = value.substring(0, value.indexOf("e"));
}
if (isZero(value)) {
return applyPrecision("0.50000000000000000000000000000", precision);
return applyPrecision("0.50000000000000000000000000000", precision, false);
} else if (value.startsWith("-")) {
return "-"+lowBoundaryForDecimal(value.substring(1), precision)+(e == null ? "" : e);
} else {
if (value.contains(".")) {
return applyPrecision(value+"50000000000000000000000000000", precision)+(e == null ? "" : e);
return applyPrecision(value+"50000000000000000000000000000", precision, false)+(e == null ? "" : e);
} else {
return applyPrecision(value+".50000000000000000000000000000", precision)+(e == null ? "" : e);
return applyPrecision(value+".50000000000000000000000000000", precision, false)+(e == null ? "" : e);
}
}
}

View File

@ -129,7 +129,7 @@ public class NpmPackageIndexBuilder {
}
}
} catch (Exception e) {
System.out.println("Error parsing "+name+": "+e.getMessage());
// System.out.println("Error parsing "+name+": "+e.getMessage());
if (name.contains("openapi")) {
return false;
}

View File

@ -1138,4 +1138,6 @@ TYPE_SPECIFIC_CHECKS_DT_XHTML_LITERAL_HREF = Hyperlink scheme ''{3}'' in ''{0}''
SM_TARGET_TYPE_UNKNOWN = The type of the target variable is not known: {0}
XHTML_XHTML_ATTRIBUTE_XML_SPACE = The attribute 'xml:space' is legal but has a fixed value of 'preserve'. It''s use is discouraged
VALIDATION_HL7_PUBLISHER_MULTIPLE_WGS = This resource has more than workgroup extension (http://hl7.org/fhir/StructureDefinition/structuredefinition-wg)
NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG = Wrong Display Name ''{0}'' for {1}#{2}. There are no valid display names found for language(s) ''{3}''. Default display is ''{4}''
NO_VALID_DISPLAY_AT_ALL = Cannot validate display Name ''{0}'' for {1}#{2}: No displays are known

View File

@ -182,7 +182,7 @@ class UtilitiesTest {
assertEquals("0.95", Utilities.lowBoundaryForDecimal("1.0", 2));
assertEquals("-1.05000000", Utilities.lowBoundaryForDecimal("-1.0", 8));
assertEquals("1.23", Utilities.lowBoundaryForDecimal("1.234", 2));
assertEquals("1.57", Utilities.lowBoundaryForDecimal("1.567", 2));
assertEquals("1.56", Utilities.lowBoundaryForDecimal("1.567", 2));
assertEquals("0.50000000", Utilities.highBoundaryForDecimal("0", 8));
assertEquals("1.500000", Utilities.highBoundaryForDecimal("1", 6));

View File

@ -497,10 +497,12 @@ public class IgLoader implements IValidationEngineLoader {
private Map<String, ByteProvider> fetchByPackage(String src, boolean loadInContext) throws FHIRException, IOException {
NpmPackage pi;
InputStream stream = directProvider.fetchByPackage(src);
if (stream != null) {
pi = NpmPackage.fromPackage(stream);
return loadPackage(pi, loadInContext);
if (directProvider != null) {
InputStream stream = directProvider.fetchByPackage(src);
if (stream != null) {
pi = NpmPackage.fromPackage(stream);
return loadPackage(pi, loadInContext);
}
}
String id = src;
String version = null;

View File

@ -277,7 +277,7 @@ public class ValueSetValidator extends BaseValidator {
}
if (version == null) {
CodeSystem cs = context.fetchCodeSystem(system);
if (cs != null && !CodeSystemUtilities.isExemptFromMultipleVersionChecking(system)) {
if (cs != null && !CodeSystemUtilities.isExemptFromMultipleVersionChecking(system) && fetcher != null) {
Set<String> possibleVersions = fetcher.fetchCanonicalResourceVersions(null, valContext.getAppContext(), system);
warning(errors, NO_RULE_DATE, IssueType.INVALID, stack, possibleVersions.size() <= 1, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_MULTIPLE_POSSIBLE_VERSIONS,
system, cs.getVersion(), CommaSeparatedStringBuilder.join(", ", Utilities.sorted(possibleVersions)));

View File

@ -163,6 +163,7 @@ public class TxTester {
return error;
}
}
private boolean runSuite(JsonObject suite, ITerminologyClient tx, List<String> modes, String filter, JsonArray output) throws FHIRFormatError, FileNotFoundException, IOException {
System.out.println("Group "+suite.asString("name"));
JsonObject outputS = new JsonObject();
@ -187,6 +188,7 @@ public class TxTester {
if (output != null) {
output.add(outputT);
}
long start = System.currentTimeMillis();
Parameters profile = loadProfile(test);
outputT.add("name", test.asString("name"));
if (Utilities.noString(filter) || filter.equals("*") || test.asString("name").contains(filter)) {
@ -219,7 +221,7 @@ public class TxTester {
throw new Exception("Unknown Operation "+test.asString("operation"));
}
System.out.println(msg == null ? "Pass" : "Fail");
System.out.println((msg == null ? "Pass" : "Fail") + " ("+Utilities.describeDuration(System.currentTimeMillis() - start)+")");
if (msg != null) {
System.out.println(" "+msg);
error = msg;

View File

@ -21,7 +21,7 @@
<commons_compress_version>1.26.0</commons_compress_version>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.5.19</validator_test_case_version>
<validator_test_case_version>1.5.20-SNAPSHOT</validator_test_case_version>
<jackson_version>2.17.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>