diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e69de29bb..e97786dae 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -0,0 +1,7 @@ +Validator: +* Add date addition/subtraction to FHIRPath +* Fix questionnaire mode parameter support for validator +* add extra debugging when valdiator can't fetch content to validate + +Other code changes: +* rename org.hl7.fhir.utilities.cache to org.hl7.fhir.utilities.npm diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/ExtensionDefinitionGenerator.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/ExtensionDefinitionGenerator.java index a5f2d838f..47a597ee3 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/ExtensionDefinitionGenerator.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/ExtensionDefinitionGenerator.java @@ -65,10 +65,10 @@ import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.utils.NPMPackageGenerator; import org.hl7.fhir.r4.utils.NPMPackageGenerator.Category; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.PackageGenerator.PackageType; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.ToolsVersion; +import org.hl7.fhir.utilities.npm.PackageGenerator.PackageType; import com.google.gson.JsonArray; import com.google.gson.JsonObject; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR3.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR3.java index 7b18144bb..758f97223 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR3.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR3.java @@ -5,7 +5,7 @@ import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.ElementDefinition; import org.hl7.fhir.dstu3.model.Resource; import org.hl7.fhir.dstu3.model.StructureDefinition; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage; import java.util.ArrayList; import java.util.List; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR4.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR4.java index 06c9aefad..8b0ed82a7 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR4.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR4.java @@ -6,7 +6,7 @@ import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.ElementDefinition; import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.StructureDefinition; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage; import java.util.ArrayList; import java.util.List; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR5.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR5.java index f051fe763..43642a52c 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR5.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/BaseLoaderR5.java @@ -8,7 +8,7 @@ import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage; import com.google.gson.JsonSyntaxException; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R2016MayToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R2016MayToR5Loader.java index 054ad92fd..4aa7b132a 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R2016MayToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R2016MayToR5Loader.java @@ -55,9 +55,9 @@ import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; -import org.hl7.fhir.utilities.cache.NpmPackage; import com.google.gson.JsonSyntaxException; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R2ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R2ToR5Loader.java index a2af79bfe..07f64c869 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R2ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R2ToR5Loader.java @@ -56,9 +56,9 @@ import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; -import org.hl7.fhir.utilities.cache.NpmPackage; import com.google.gson.JsonSyntaxException; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R3ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R3ToR5Loader.java index 24439f069..07feac9ab 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R3ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R3ToR5Loader.java @@ -56,9 +56,9 @@ import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; -import org.hl7.fhir.utilities.cache.NpmPackage; import com.google.gson.JsonSyntaxException; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R4ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R4ToR5Loader.java index 8df1eef65..fa8af1b5f 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R4ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R4ToR5Loader.java @@ -56,9 +56,9 @@ import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; -import org.hl7.fhir.utilities.cache.NpmPackage; import com.google.gson.JsonSyntaxException; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R5ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R5ToR5Loader.java index dd67566e1..a71cb3705 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R5ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/R5ToR5Loader.java @@ -55,9 +55,9 @@ import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; -import org.hl7.fhir.utilities.cache.NpmPackage; import com.google.gson.JsonSyntaxException; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NUCCConvertor.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NUCCConvertor.java index 777768c59..7c677c3da 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NUCCConvertor.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NUCCConvertor.java @@ -35,30 +35,31 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import org.hl7.fhir.dstu3.formats.IParser.OutputStyle; -import org.hl7.fhir.dstu3.formats.XmlParser; -import org.hl7.fhir.dstu3.model.CodeSystem; -import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; -import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemHierarchyMeaning; -import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; -import org.hl7.fhir.dstu3.model.DateTimeType; -import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; -import org.hl7.fhir.dstu3.terminologies.CodeSystemUtilities; -import org.hl7.fhir.dstu3.utils.ToolingExtensions; +import org.hl7.fhir.r4.formats.IParser.OutputStyle; +import org.hl7.fhir.r4.formats.XmlParser; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; +import org.hl7.fhir.r4.model.CodeSystem.CodeSystemHierarchyMeaning; +import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.r4.model.CodeSystem.PropertyType; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.terminologies.CodeSystemUtilities; +import org.hl7.fhir.r4.utils.ToolingExtensions; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r4.formats.JsonParser; import org.hl7.fhir.utilities.CSVReader; import org.hl7.fhir.utilities.Utilities; public class NUCCConvertor { - private String[] last = new String[2]; - private ConceptDefinitionComponent[] concepts = new ConceptDefinitionComponent[2]; public static void main(String[] args) throws Exception { new NUCCConvertor().execute(); } - public void execute() throws IOException, FHIRException { + public void execute() throws IOException, FHIRException { CSVReader csv = new CSVReader(new FileInputStream("c:\\temp\\nucc.csv")); CodeSystem cs = new CodeSystem(); cs.setId("nucc-provider-taxonomy"); @@ -66,50 +67,41 @@ public class NUCCConvertor { cs.setName("NUCC Provider Taxonomy"); cs.setDateElement(new DateTimeType()); cs.setDescription("The Health Care Provider Taxonomy code is a unique alphanumeric code, ten characters in length. The code set is structured into three distinct 'Levels' including Provider Type, Classification, and Area of Specialization"); - cs.setCopyright("See NUCC copyright statement"); + cs.setCopyright("Vendors must request a license to include this in a product per the following: 'Vendors interested in incorporating the Health Care Provider Taxonomy code set into their commercial products must complete the license request form found on the CSV page.' Using the form at the url listed. The preamble is reproduced below: http://www.nucc.org/index.php?option=com_content&view=article&id=111&Itemid=110"); cs.setStatus(PublicationStatus.ACTIVE); cs.setContent(CodeSystemContentMode.COMPLETE); cs.setExperimental(false); cs.setValueSet("http://hl7.org/fhir/ValueSet/nucc-provider-taxonomy"); cs.setHierarchyMeaning(CodeSystemHierarchyMeaning.CLASSIFIEDWITH); + cs.addProperty().setCode("grouping").setType(PropertyType.STRING).setDescription("A major grouping of service(s) or occupation(s) of health care providers. For example: Allopathic & Osteopathic Physicians, Dental Providers, Hospitals, etc"); + cs.addProperty().setCode("classification").setType(PropertyType.STRING).setDescription("A more specific service or occupation related to the Provider Grouping.e"); + cs.addProperty().setCode("specialization").setType(PropertyType.STRING).setDescription("A more specialized area of the Classification in which a provider chooses to practice or make services available."); csv.parseLine(); - while (csv.ready()) - { + while (csv.ready()) { String[] values = csv.parseLine(); processLine(cs, values); } csv.close(); - new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream("c:\\temp\\nucc.xml"), cs); + cs.sort(); + new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream("c:\\temp\\nucc.json"), cs); } private void processLine(CodeSystem cs, String[] values) throws FHIRFormatError { - if (!values[1].equals(last[0])) { - last[1] = ""; - last[0] = values[1]; - concepts[0] = new ConceptDefinitionComponent(); - cs.getConcept().add(concepts[0]); - concepts[0].setDisplay(values[1]); - concepts[0].setCode("base-"+Integer.toString(cs.getConcept().size())); - CodeSystemUtilities.setNotSelectable(cs, concepts[0]); + ConceptDefinitionComponent cc = new ConceptDefinitionComponent(); + cs.getConcept().add(cc); + cc.setCode(values[0]); + cc.setDisplay(values[4]); + if (!Utilities.noString(values[1])) { + cc.addProperty().setCode("grouping").setValue(new StringType(values[1])); } - if (!values[2].equals(last[1])) { - last[1] = values[2]; - concepts[1] = new ConceptDefinitionComponent(); - concepts[0].getConcept().add(concepts[1]); - concepts[1].setCode(values[0]); - concepts[1].setDisplay(values[2]); - concepts[1].setDefinition(values[4]); - if (values.length > 5 && !Utilities.noString(values[5])) - ToolingExtensions.addCSComment(concepts[1], values[5]); - } else if (!Utilities.noString(values[3])) { - ConceptDefinitionComponent cc = new ConceptDefinitionComponent(); - concepts[1].getConcept().add(cc); - cc.setCode(values[0]); - cc.setDisplay(values[3]); - cc.setDefinition(values[4]); - if (values.length > 5 && !Utilities.noString(values[5])) - ToolingExtensions.addCSComment(cc, values[5]); + if (!Utilities.noString(values[2])) { + cc.addProperty().setCode("classification").setValue(new StringType(values[2])); } + if (!Utilities.noString(values[3])) { + cc.addProperty().setCode("specialization").setValue(new StringType(values[3])); + } + if (values.length > 5 && !Utilities.noString(values[5])) + cc.setDefinition(values[5]); } - + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java index f5da11936..4f63e27fa 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java @@ -33,9 +33,9 @@ import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.NpmPackageIndexBuilder; import org.hl7.fhir.utilities.json.JSONUtil; import org.hl7.fhir.utilities.json.JsonTrackingParser; +import org.hl7.fhir.utilities.npm.NpmPackageIndexBuilder; import com.google.common.base.Charsets; import com.google.gson.JsonArray; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/OIDBasedValueSetImporter.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/OIDBasedValueSetImporter.java index 020a643e2..87475bfc1 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/OIDBasedValueSetImporter.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/OIDBasedValueSetImporter.java @@ -12,9 +12,9 @@ import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.SimpleWorkerContext; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.w3c.dom.Document; public class OIDBasedValueSetImporter { diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PhinVadsImporter.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PhinVadsImporter.java index 1f9a3db56..deafaf349 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PhinVadsImporter.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PhinVadsImporter.java @@ -19,9 +19,9 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.utilities.CSVReader; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.ToolsVersion; public class PhinVadsImporter extends OIDBasedValueSetImporter { diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XMLPackageConvertor.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XMLPackageConvertor.java index 67d0e97ac..c359c4d71 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XMLPackageConvertor.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XMLPackageConvertor.java @@ -6,7 +6,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.Map.Entry; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage; import com.google.gson.JsonElement; import com.google.gson.JsonObject; diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/SimpleWorkerContext.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/SimpleWorkerContext.java index 18b4833e7..080ed1952 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/SimpleWorkerContext.java @@ -89,7 +89,7 @@ import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.utilities.CSFileInputStream; import org.hl7.fhir.utilities.OIDUtils; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/SimpleWorkerContext.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/SimpleWorkerContext.java index 78718cce6..72a0f050c 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/SimpleWorkerContext.java @@ -79,7 +79,7 @@ import org.hl7.fhir.r4.utils.IResourceValidator; import org.hl7.fhir.r4.utils.NarrativeGenerator; import org.hl7.fhir.utilities.CSFileInputStream; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/BaseDateTimeType.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/BaseDateTimeType.java index 04c9a3429..699db4c0e 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/BaseDateTimeType.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/BaseDateTimeType.java @@ -939,4 +939,9 @@ public abstract class BaseDateTimeType extends PrimitiveType { } + @Override + public String fpValue() { + return "@"+primitiveValue(); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/CodeSystem.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/CodeSystem.java index 946f80d2e..43a1b69ea 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/CodeSystem.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/CodeSystem.java @@ -37,6 +37,7 @@ package org.hl7.fhir.r4.model; import java.util.*; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponentSorter; import org.hl7.fhir.r4.model.Enumerations.*; import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.api.annotation.SearchParamDefinition; @@ -5049,6 +5050,30 @@ public class CodeSystem extends MetadataResource { return null; } + public class ConceptDefinitionComponentSorter implements Comparator { + @Override + public int compare(ConceptDefinitionComponent l, ConceptDefinitionComponent r) { + return l.getCode().compareTo(r.getCode()); + } + } + + public void sort() { + sort(getConcept(), new ConceptDefinitionComponentSorter()); + } + + public void sort(List list, Comparator comp) { + Collections.sort(list, comp); + for (ConceptDefinitionComponent def : list) { + if (def.hasConcept()) { + sort (def.getConcept(), comp); + } + } + } + + public void sort(Comparator comp) { + sort(getConcept(), comp); + } + // end addition } \ No newline at end of file diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java index a061937a9..3ed27a087 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java @@ -34,6 +34,7 @@ package org.hl7.fhir.r4.model; import java.util.ArrayList; import java.util.List; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; public class ExpressionNode { @@ -41,31 +42,6 @@ public class ExpressionNode { public enum Kind { Name, Function, Constant, Group, Unary } - public static class SourceLocation { - private int line; - private int column; - public SourceLocation(int line, int column) { - super(); - this.line = line; - this.column = column; - } - public int getLine() { - return line; - } - public int getColumn() { - return column; - } - public void setLine(int line) { - this.line = line; - } - public void setColumn(int column) { - this.column = column; - } - - public String toString() { - return Integer.toString(line)+", "+Integer.toString(column); - } - } public enum Function { Custom, diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/PrimitiveType.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/PrimitiveType.java index 30df38036..4b8a95ca8 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/PrimitiveType.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/PrimitiveType.java @@ -249,4 +249,7 @@ public abstract class PrimitiveType extends Type implements IPrimitiveType return StringUtils.isNotBlank(getValueAsString()); } + public String fpValue() { + return primitiveValue(); + } } \ No newline at end of file diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/CodeSystemUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/CodeSystemUtilities.java index da74a6e0f..f4b21442b 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/CodeSystemUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/CodeSystemUtilities.java @@ -67,7 +67,7 @@ public class CodeSystemUtilities { public static void setNotSelectable(CodeSystem cs, ConceptDefinitionComponent concept) throws FHIRFormatError { defineNotSelectableProperty(cs); - ConceptPropertyComponent p = getProperty(concept, "notSelectable"); + ConceptPropertyComponent p = getProperty(concept, "abstract"); if (p != null) p.setValue(new BooleanType(true)); else diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java index 019e639df..5fb2074a6 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java @@ -50,8 +50,8 @@ import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.utilities.CSFile; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRLexer.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRLexer.java index 38df5e9a4..83619c66b 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRLexer.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRLexer.java @@ -34,7 +34,7 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.model.ExpressionNode; -import org.hl7.fhir.r4.model.ExpressionNode.SourceLocation; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; // shared lexer for concrete syntaxes diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java index aedd55871..7bc7ec1c0 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java @@ -19,6 +19,7 @@ import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r4.model.TypeDetails.ProfiledType; import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.Utilities; @@ -414,10 +415,10 @@ public class FHIRPathEngine { } StructureDefinition sd = worker.fetchResource(StructureDefinition.class, ctxt); if (sd == null) - throw new PathEngineException("Unknown context "+context); - ElementDefinitionMatch ed = getElementDefinition(sd, context, true); + throw new PathEngineException("Unknown context "+context, expr.getStart(), expr.toString()); + ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); if (ed == null) - throw new PathEngineException("Unknown context element "+context); + throw new PathEngineException("Unknown context element "+context, expr.getStart(), expr.toString()); if (ed.fixedType != null) types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); else if (ed.getDefinition().getType().isEmpty() || isAbstractType(ed.getDefinition().getType())) @@ -438,9 +439,9 @@ public class FHIRPathEngine { if (!context.contains(".")) { types = new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()); } else { - ElementDefinitionMatch ed = getElementDefinition(sd, context, true); + ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); if (ed == null) - throw new PathEngineException("Unknown context element "+context); + throw new PathEngineException("Unknown context element "+context, expr.getStart(), expr.toString()); if (ed.fixedType != null) types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); else if (ed.getDefinition().getType().isEmpty() || isAbstractType(ed.getDefinition().getType())) @@ -1161,7 +1162,7 @@ public class FHIRPathEngine { work.addAll(work2); break; case Constant: - Base b = resolveConstant(context, exp.getConstant(), false); + Base b = resolveConstant(context, exp.getConstant(), false, exp); if (b != null) work.add(b); break; @@ -1177,15 +1178,15 @@ public class FHIRPathEngine { ExpressionNode next = exp.getOpNext(); ExpressionNode last = exp; while (next != null) { - List work2 = preOperate(work, last.getOperation()); + List work2 = preOperate(work, last.getOperation(), exp); if (work2 != null) work = work2; else if (last.getOperation() == Operation.Is || last.getOperation() == Operation.As) { work2 = executeTypeName(context, focus, next, false); - work = operate(context, work, last.getOperation(), work2); + work = operate(context, work, last.getOperation(), work2, last); } else { work2 = execute(context, focus, next, true); - work = operate(context, work, last.getOperation(), work2); + work = operate(context, work, last.getOperation(), work2, last); // System.out.println("Result of {'"+last.toString()+" "+last.getOperation().toCode()+" "+next.toString()+"'}: "+focus.toString()); } last = next; @@ -1206,7 +1207,7 @@ public class FHIRPathEngine { } - private List preOperate(List left, Operation operation) throws PathEngineException { + private List preOperate(List left, Operation operation, ExpressionNode expr) throws PathEngineException { if (left.size() == 0) return null; switch (operation) { @@ -1215,7 +1216,7 @@ public class FHIRPathEngine { case Or: return isBoolean(left, true) ? makeBoolean(true) : null; case Implies: - Equality v = asBool(left); + Equality v = asBool(left, expr); return v == Equality.False ? makeBoolean(true) : null; default: return null; @@ -1246,13 +1247,13 @@ public class FHIRPathEngine { else if (atEntry && exp.getName().equals("$total")) result.update(anything(CollectionStatus.UNORDERED)); else if (atEntry && focus == null) - result.update(executeContextType(context, exp.getName())); + result.update(executeContextType(context, exp.getName(), exp)); else { for (String s : focus.getTypes()) { result.update(executeType(s, exp, atEntry)); } if (result.hasNoTypes()) - throw new PathEngineException("The name "+exp.getName()+" is not valid for any of the possible types: "+focus.describe()); + throw new PathEngineException("The name "+exp.getName()+" is not valid for any of the possible types: "+focus.describe(), exp.getStart(), exp.toString()); } break; case Function: @@ -1262,7 +1263,7 @@ public class FHIRPathEngine { result.addType("integer"); break; case Constant: - result.update(resolveConstantType(context, exp.getConstant())); + result.update(resolveConstantType(context, exp.getConstant(), exp)); break; case Group: result.update(executeType(context, focus, exp.getGroup(), atEntry)); @@ -1282,7 +1283,7 @@ public class FHIRPathEngine { work = executeTypeName(context, focus, next, atEntry); else work = executeType(context, focus, next, atEntry); - result = operateTypes(result, last.getOperation(), work); + result = operateTypes(result, last.getOperation(), work, exp); last = next; next = next.getOpNext(); } @@ -1291,16 +1292,16 @@ public class FHIRPathEngine { return result; } - private Base resolveConstant(ExecutionContext context, Base constant, boolean beforeContext) throws PathEngineException { + private Base resolveConstant(ExecutionContext context, Base constant, boolean beforeContext, ExpressionNode expr) throws PathEngineException { if (!(constant instanceof FHIRConstant)) return constant; FHIRConstant c = (FHIRConstant) constant; if (c.getValue().startsWith("%")) { - return resolveConstant(context, c.getValue(), beforeContext); + return resolveConstant(context, c.getValue(), beforeContext, expr); } else if (c.getValue().startsWith("@")) { return processDateConstant(context.appInfo, c.getValue().substring(1)); } else - throw new PathEngineException("Invaild FHIR Constant "+c.getValue()); + throw new PathEngineException("Invaild FHIR Constant "+c.getValue(), expr.getStart(), expr.toString()); } private Base processDateConstant(Object appInfo, String value) throws PathEngineException { @@ -1322,7 +1323,7 @@ public class FHIRPathEngine { } - private Base resolveConstant(ExecutionContext context, String s, boolean beforeContext) throws PathEngineException { + private Base resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr) throws PathEngineException { if (s.equals("%sct")) return new StringType("http://snomed.info/sct").noExtensions(); else if (s.equals("%loinc")) @@ -1331,11 +1332,11 @@ public class FHIRPathEngine { return new StringType("http://unitsofmeasure.org").noExtensions(); else if (s.equals("%resource")) { if (context.focusResource == null) - throw new PathEngineException("Cannot use %resource in this context"); + throw new PathEngineException("Cannot use %resource in this context", expr.getStart(), expr.toString()); return context.focusResource; } else if (s.equals("%rootResource")) { if (context.rootResource == null) - throw new PathEngineException("Cannot use %rootResource in this context"); + throw new PathEngineException("Cannot use %rootResource in this context", expr.getStart(), expr.toString()); return context.rootResource; } else if (s.equals("%context")) { return context.context; @@ -1348,7 +1349,7 @@ public class FHIRPathEngine { else if (s.startsWith("%`ext-")) return new StringType("http://hl7.org/fhir/StructureDefinition/"+s.substring(6, s.length()-1)).noExtensions(); else if (hostServices == null) - throw new PathEngineException("Unknown fixed constant '"+s+"'"); + throw new PathEngineException("Unknown fixed constant '"+s+"'", expr.getStart(), expr.toString()); else return hostServices.resolveConstant(context.appInfo, s.substring(1), beforeContext); } @@ -1408,39 +1409,39 @@ public class FHIRPathEngine { } - private List operate(ExecutionContext context, List left, Operation operation, List right) throws FHIRException { + private List operate(ExecutionContext context, List left, Operation operation, List right, ExpressionNode expr) throws FHIRException { switch (operation) { case Equals: return opEquals(left, right); - case Equivalent: return opEquivalent(left, right); - case NotEquals: return opNotEquals(left, right); - case NotEquivalent: return opNotEquivalent(left, right); - case LessThan: return opLessThan(left, right); - case Greater: return opGreater(left, right); - case LessOrEqual: return opLessOrEqual(left, right); - case GreaterOrEqual: return opGreaterOrEqual(left, right); - case Union: return opUnion(left, right); - case In: return opIn(left, right); - case MemberOf: return opMemberOf(context, left, right); - case Contains: return opContains(left, right); - case Or: return opOr(left, right); - case And: return opAnd(left, right); - case Xor: return opXor(left, right); - case Implies: return opImplies(left, right); - case Plus: return opPlus(left, right); - case Times: return opTimes(left, right); - case Minus: return opMinus(left, right); - case Concatenate: return opConcatenate(left, right); - case DivideBy: return opDivideBy(left, right); - case Div: return opDiv(left, right); - case Mod: return opMod(left, right); - case Is: return opIs(left, right); - case As: return opAs(left, right); + case Equivalent: return opEquivalent(left, right, expr); + case NotEquals: return opNotEquals(left, right, expr); + case NotEquivalent: return opNotEquivalent(left, right, expr); + case LessThan: return opLessThan(left, right, expr); + case Greater: return opGreater(left, right, expr); + case LessOrEqual: return opLessOrEqual(left, right, expr); + case GreaterOrEqual: return opGreaterOrEqual(left, right, expr); + case Union: return opUnion(left, right, expr); + case In: return opIn(left, right, expr); + case MemberOf: return opMemberOf(context, left, right, expr); + case Contains: return opContains(left, right, expr); + case Or: return opOr(left, right, expr); + case And: return opAnd(left, right, expr); + case Xor: return opXor(left, right, expr); + case Implies: return opImplies(left, right, expr); + case Plus: return opPlus(left, right, expr); + case Times: return opTimes(left, right, expr); + case Minus: return opMinus(left, right, expr); + case Concatenate: return opConcatenate(left, right, expr); + case DivideBy: return opDivideBy(left, right, expr); + case Div: return opDiv(left, right, expr); + case Mod: return opMod(left, right, expr); + case Is: return opIs(left, right, expr); + case As: return opAs(left, right, expr); default: throw new Error("Not Done Yet: "+operation.toCode()); } } - private List opAs(List left, List right) { + private List opAs(List left, List right, ExpressionNode expr) { List result = new ArrayList<>(); if (right.size() != 1) return result; @@ -1455,7 +1456,7 @@ public class FHIRPathEngine { } - private List opIs(List left, List right) { + private List opIs(List left, List right, ExpressionNode expr) { List result = new ArrayList(); if (left.size() != 1 || right.size() != 1) result.add(new BooleanType(false).noExtensions()); @@ -1472,7 +1473,7 @@ public class FHIRPathEngine { } - private TypeDetails operateTypes(TypeDetails left, Operation operation, TypeDetails right) { + private TypeDetails operateTypes(TypeDetails left, Operation operation, TypeDetails right, ExpressionNode expr) { switch (operation) { case Equals: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Equivalent: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); @@ -1514,6 +1515,13 @@ public class FHIRPathEngine { result.addType(TypeDetails.FP_Decimal); else if (left.hasType(worker, "string", "id", "code", "uri") && right.hasType(worker, "string", "id", "code", "uri")) result.addType(TypeDetails.FP_String); + else if (left.hasType(worker, "date", "dateTime", "instant")) { + if (right.hasType(worker, "Quantity")) { + result.addType(left.getType()); + } else { + throw new PathEngineException(String.format("Error in date arithmetic: Unable to add type {0} to {1}", right.getType(), left.getType()), expr.getStart(), expr.toString()); + } + } return result; case Minus: result = new TypeDetails(CollectionStatus.SINGLETON); @@ -1521,6 +1529,13 @@ public class FHIRPathEngine { result.addType(TypeDetails.FP_Integer); else if (left.hasType(worker, "integer", "decimal") && right.hasType(worker, "integer", "decimal")) result.addType(TypeDetails.FP_Decimal); + else if (left.hasType(worker, "date", "dateTime", "instant")) { + if (right.hasType(worker, "Quantity")) { + result.addType(left.getType()); + } else { + throw new PathEngineException(String.format("Error in date arithmetic: Unable to subtract type {0} from {1}", right.getType(), left.getType())); + } + } return result; case Div: case Mod: @@ -1565,7 +1580,7 @@ public class FHIRPathEngine { return makeBoolean(res); } - private List opNotEquals(List left, List right) { + private List opNotEquals(List left, List right, ExpressionNode expr) { if (!legacyMode && (left.size() == 0 || right.size() == 0)) return new ArrayList(); @@ -1634,9 +1649,9 @@ public class FHIRPathEngine { } - private boolean doEquivalent(Base left, Base right) throws PathEngineException { + private boolean doEquivalent(Base left, Base right, ExpressionNode expr) throws PathEngineException { if (left instanceof Quantity && right instanceof Quantity) - return qtyEquivalent((Quantity) left, (Quantity) right); + return qtyEquivalent((Quantity) left, (Quantity) right, expr); if (left.hasType("integer") && right.hasType("integer")) return doEquals(left, right); if (left.hasType("boolean") && right.hasType("boolean")) @@ -1648,7 +1663,7 @@ public class FHIRPathEngine { if (left.hasType(FHIR_TYPES_STRING) && right.hasType(FHIR_TYPES_STRING)) return Utilities.equivalent(convertToString(left), convertToString(right)); - throw new PathEngineException(String.format("Unable to determine equivalence between %s and %s", left.fhirType(), right.fhirType())); + throw new PathEngineException(String.format("Unable to determine equivalence between %s and %s", left.fhirType(), right.fhirType()), expr.getStart(), expr.toString()); } private boolean qtyEqual(Quantity left, Quantity right) { @@ -1689,19 +1704,19 @@ public class FHIRPathEngine { } - private boolean qtyEquivalent(Quantity left, Quantity right) throws PathEngineException { + private boolean qtyEquivalent(Quantity left, Quantity right, ExpressionNode expr) throws PathEngineException { if (worker.getUcumService() != null) { DecimalType dl = qtyToCanonical(left); DecimalType dr = qtyToCanonical(right); if (dl != null && dr != null) - return doEquivalent(dl, dr); + return doEquivalent(dl, dr, expr); } return left.equals(right); } - private List opEquivalent(List left, List right) throws PathEngineException { + private List opEquivalent(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() != right.size()) return makeBoolean(false); @@ -1709,7 +1724,7 @@ public class FHIRPathEngine { for (int i = 0; i < left.size(); i++) { boolean found = false; for (int j = 0; j < right.size(); j++) { - if (doEquivalent(left.get(i), right.get(j))) { + if (doEquivalent(left.get(i), right.get(j), expr)) { found = true; break; } @@ -1722,7 +1737,7 @@ public class FHIRPathEngine { return makeBoolean(res); } - private List opNotEquivalent(List left, List right) throws PathEngineException { + private List opNotEquivalent(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() != right.size()) return makeBoolean(true); @@ -1730,7 +1745,7 @@ public class FHIRPathEngine { for (int i = 0; i < left.size(); i++) { boolean found = false; for (int j = 0; j < right.size(); j++) { - if (doEquivalent(left.get(i), right.get(j))) { + if (doEquivalent(left.get(i), right.get(j), expr)) { found = true; break; } @@ -1745,7 +1760,7 @@ public class FHIRPathEngine { private final static String[] FHIR_TYPES_STRING = new String[] {"string", "uri", "code", "oid", "id", "uuid", "sid", "markdown", "base64Binary", "canonical", "url"}; - private List opLessThan(List left, List right) throws FHIRException { + private List opLessThan(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); @@ -1761,12 +1776,12 @@ public class FHIRPathEngine { else if ((l.hasType("time")) && (r.hasType("time"))) return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) < 0); else - throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType()); + throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType(), expr.getStart(), expr.toString()); } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("code"); List rUnit = right.get(0).listChildrenByName("code"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) return makeBoolean(false); @@ -1775,14 +1790,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonical((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonical((Quantity) right.get(0))); - return opLessThan(dl, dr); + return opLessThan(dl, dr, expr); } } } return new ArrayList(); } - private List opGreater(List left, List right) throws FHIRException { + private List opGreater(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { @@ -1797,12 +1812,12 @@ public class FHIRPathEngine { else if ((l.hasType("time")) && (r.hasType("time"))) return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) > 0); else - throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType()); + throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType(), expr.getStart(), expr.toString()); } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("unit"); List rUnit = right.get(0).listChildrenByName("unit"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opGreater(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opGreater(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) return makeBoolean(false); @@ -1811,14 +1826,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonical((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonical((Quantity) right.get(0))); - return opGreater(dl, dr); + return opGreater(dl, dr, expr); } } } return new ArrayList(); } - private List opLessOrEqual(List left, List right) throws FHIRException { + private List opLessOrEqual(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { @@ -1833,14 +1848,14 @@ public class FHIRPathEngine { else if ((l.hasType("time")) && (r.hasType("time"))) return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) <= 0); else - throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType()); + throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType(), expr.getStart(), expr.toString()); } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnits = left.get(0).listChildrenByName("unit"); String lunit = lUnits.size() == 1 ? lUnits.get(0).primitiveValue() : null; List rUnits = right.get(0).listChildrenByName("unit"); String runit = rUnits.size() == 1 ? rUnits.get(0).primitiveValue() : null; if ((lunit == null && runit == null) || lunit.equals(runit)) { - return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) return makeBoolean(false); @@ -1849,14 +1864,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonical((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonical((Quantity) right.get(0))); - return opLessOrEqual(dl, dr); + return opLessOrEqual(dl, dr, expr); } } } return new ArrayList(); } - private List opGreaterOrEqual(List left, List right) throws FHIRException { + private List opGreaterOrEqual(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { @@ -1871,12 +1886,12 @@ public class FHIRPathEngine { else if ((l.hasType("time")) && (r.hasType("time"))) return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) >= 0); else - throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType()); + throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType(), expr.getStart(), expr.toString()); } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("unit"); List rUnit = right.get(0).listChildrenByName("unit"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opGreaterOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opGreaterOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) return makeBoolean(false); @@ -1885,14 +1900,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonical((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonical((Quantity) right.get(0))); - return opGreaterOrEqual(dl, dr); + return opGreaterOrEqual(dl, dr, expr); } } } return new ArrayList(); } - private List opMemberOf(ExecutionContext context, List left, List right) throws FHIRException { + private List opMemberOf(ExecutionContext context, List left, List right, ExpressionNode expr) throws FHIRException { boolean ans = false; ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, right.get(0).primitiveValue()) : worker.fetchResource(ValueSet.class, right.get(0).primitiveValue()); if (vs != null) { @@ -1912,7 +1927,7 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opIn(List left, List right) throws FHIRException { + private List opIn(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0) return new ArrayList(); if (right.size() == 0) @@ -1935,7 +1950,7 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opContains(List left, List right) { + private List opContains(List left, List right, ExpressionNode expr) { if (left.size() == 0 || right.size() == 0) return new ArrayList(); boolean ans = true; @@ -1956,17 +1971,17 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opPlus(List left, List right) throws PathEngineException { + private List opPlus(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing +: left operand has more than one value"); + throw new PathEngineException("Error performing +: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing +: right operand has more than one value"); - if (!right.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing +: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException("Error performing +: right operand has more than one value", expr.getStart(), expr.toString()); + if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) + throw new PathEngineException(String.format("Error performing +: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -1977,22 +1992,78 @@ public class FHIRPathEngine { result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) + Integer.parseInt(r.primitiveValue()))); else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) result.add(new DecimalType(new BigDecimal(l.primitiveValue()).add(new BigDecimal(r.primitiveValue())))); + else if (l.isDateTime() && r.hasType("Quantity")) + result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, false, expr)); else - throw new PathEngineException(String.format("Error performing +: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing +: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private List opTimes(List left, List right) throws PathEngineException { + private BaseDateTimeType dateAdd(BaseDateTimeType d, Quantity q, boolean negate, ExpressionNode expr) { + BaseDateTimeType result = (BaseDateTimeType) d.copy(); + + int value = negate ? 0 - q.getValue().intValue() : q.getValue().intValue(); + switch (q.hasCode() ? q.getCode() : q.getUnit()) { + case "years": + case "year": + result.add(Calendar.YEAR, value); + break; + case "a": + throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode()), expr.getStart(), expr.toString()); + case "months": + case "month": + result.add(Calendar.MONTH, value); + break; + case "mo": + throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode()), expr.getStart(), expr.toString()); + case "weeks": + case "week": + case "wk": + result.add(Calendar.DAY_OF_MONTH, value * 7); + break; + case "days": + case "day": + case "d": + result.add(Calendar.DAY_OF_MONTH, value); + break; + case "hours": + case "hour": + case "h": + result.add(Calendar.HOUR, value); + break; + case "minutes": + case "minute": + case "min": + result.add(Calendar.MINUTE, value); + break; + case "seconds": + case "second": + case "s": + result.add(Calendar.SECOND, value); + break; + case "milliseconds": + case "millisecond": + case "ms": + result.add(Calendar.MILLISECOND, value); + break; + default: + throw new PathEngineException(String.format("Error in date arithmetic: unrecognized time unit %s", q.getCode()), expr.getStart(), expr.toString()); + } + return result; + } + + + private List opTimes(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing *: left operand has more than one value"); + throw new PathEngineException("Error performing *: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing *: right operand has more than one value"); + throw new PathEngineException("Error performing *: right operand has more than one value", expr.getStart(), expr.toString()); if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing *: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing *: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2010,23 +2081,23 @@ public class FHIRPathEngine { p = worker.getUcumService().multiply(pl, pr); result.add(pairToQty(p)); } catch (UcumException e) { - throw new PathEngineException(e.getMessage(), e); + throw new PathEngineException(e.getMessage(), expr.getOpStart(), expr.toString(), e); } } else - throw new PathEngineException(String.format("Error performing *: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing *: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private List opConcatenate(List left, List right) throws PathEngineException { + private List opConcatenate(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() > 1) - throw new PathEngineException("Error performing &: left operand has more than one value"); + throw new PathEngineException("Error performing &: left operand has more than one value", expr.getStart(), expr.toString()); if (left.size() > 0 && !left.get(0).hasType(FHIR_TYPES_STRING)) - throw new PathEngineException(String.format("Error performing &: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing &: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing &: right operand has more than one value"); + throw new PathEngineException("Error performing &: right operand has more than one value", expr.getStart(), expr.toString()); if (right.size() > 0 && !right.get(0).hasType(FHIR_TYPES_STRING)) - throw new PathEngineException(String.format("Error performing &: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing &: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); String l = left.size() == 0 ? "" : left.get(0).primitiveValue(); @@ -2035,7 +2106,7 @@ public class FHIRPathEngine { return result; } - private List opUnion(List left, List right) { + private List opUnion(List left, List right, ExpressionNode expr) { List result = new ArrayList(); for (Base item : left) { if (!doContains(result, item)) @@ -2058,9 +2129,9 @@ public class FHIRPathEngine { } - private List opAnd(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opAnd(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case False: return makeBoolean(false); case Null: @@ -2082,9 +2153,9 @@ public class FHIRPathEngine { return list.size() == 1 && list.get(0) instanceof BooleanType && ((BooleanType) list.get(0)).booleanValue() == b; } - private List opOr(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opOr(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case True: return makeBoolean(true); case Null: @@ -2102,9 +2173,9 @@ public class FHIRPathEngine { return makeNull(); } - private List opXor(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opXor(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case True: switch (r) { @@ -2124,13 +2195,13 @@ public class FHIRPathEngine { return makeNull(); } - private List opImplies(List left, List right) throws PathEngineException { - Equality eq = asBool(left); + private List opImplies(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality eq = asBool(left, expr); if (eq == Equality.False) return makeBoolean(true); else if (right.size() == 0) return makeNull(); - else switch (asBool(right)) { + else switch (asBool(right, expr)) { case False: return eq == Equality.Null ? makeNull() : makeBoolean(false); case Null: return makeNull(); case True: return makeBoolean(true); @@ -2139,17 +2210,17 @@ public class FHIRPathEngine { } - private List opMinus(List left, List right) throws PathEngineException { + private List opMinus(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing -: left operand has more than one value"); + throw new PathEngineException("Error performing -: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing -: right operand has more than one value"); - if (!right.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing -: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException("Error performing -: right operand has more than one value", expr.getStart(), expr.toString()); + if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) + throw new PathEngineException(String.format("Error performing -: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2159,22 +2230,24 @@ public class FHIRPathEngine { result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) - Integer.parseInt(r.primitiveValue()))); else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) result.add(new DecimalType(new BigDecimal(l.primitiveValue()).subtract(new BigDecimal(r.primitiveValue())))); + else if (l.isDateTime() && r.hasType("Quantity")) + result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, true, expr)); else - throw new PathEngineException(String.format("Error performing -: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing -: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private List opDivideBy(List left, List right) throws PathEngineException { + private List opDivideBy(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing /: left operand has more than one value"); + throw new PathEngineException("Error performing /: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) throw new PathEngineException("Error performing /: right operand has more than one value"); if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing /: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing /: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2197,24 +2270,24 @@ public class FHIRPathEngine { p = worker.getUcumService().multiply(pl, pr); result.add(pairToQty(p)); } catch (UcumException e) { - throw new PathEngineException(e.getMessage(), e); + throw new PathEngineException(e.getMessage(), expr.getOpStart(), expr.toString(), e); } } else - throw new PathEngineException(String.format("Error performing /: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing /: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private List opDiv(List left, List right) throws PathEngineException { + private List opDiv(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing div: left operand has more than one value"); + throw new PathEngineException("Error performing div: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing div: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing div: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing div: right operand has more than one value"); + throw new PathEngineException("Error performing div: right operand has more than one value", expr.getStart(), expr.toString()); if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) - throw new PathEngineException(String.format("Error performing div: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing div: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2233,21 +2306,21 @@ public class FHIRPathEngine { } } else - throw new PathEngineException(String.format("Error performing div: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing div: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private List opMod(List left, List right) throws PathEngineException { + private List opMod(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() > 1) - throw new PathEngineException("Error performing mod: left operand has more than one value"); + throw new PathEngineException("Error performing mod: left operand has more than one value", expr.getStart(), expr.toString()); if (!left.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing mod: left operand has the wrong type (%s)", left.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing mod: left operand has the wrong type (%s)", left.get(0).fhirType()), expr.getStart(), expr.toString()); if (right.size() > 1) - throw new PathEngineException("Error performing mod: right operand has more than one value"); + throw new PathEngineException("Error performing mod: right operand has more than one value", expr.getStart(), expr.toString()); if (!right.get(0).isPrimitive()) - throw new PathEngineException(String.format("Error performing mod: right operand has the wrong type (%s)", right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing mod: right operand has the wrong type (%s)", right.get(0).fhirType()), expr.getStart(), expr.toString()); List result = new ArrayList(); Base l = left.get(0); @@ -2266,12 +2339,12 @@ public class FHIRPathEngine { } } else - throw new PathEngineException(String.format("Error performing mod: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType())); + throw new PathEngineException(String.format("Error performing mod: left and right operand have incompatible or illegal types (%s, %s)", left.get(0).fhirType(), right.get(0).fhirType()), expr.getStart(), expr.toString()); return result; } - private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr) throws PathEngineException { if (constant instanceof BooleanType) return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); else if (constant instanceof IntegerType) @@ -2281,12 +2354,12 @@ public class FHIRPathEngine { else if (constant instanceof Quantity) return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); else if (constant instanceof FHIRConstant) - return resolveConstantType(context, ((FHIRConstant) constant).getValue()); + return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr); else return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } - private TypeDetails resolveConstantType(ExecutionTypeContext context, String s) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr) throws PathEngineException { if (s.startsWith("@")) { if (s.startsWith("@T")) return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); @@ -2300,11 +2373,11 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); else if (s.equals("%resource")) { if (context.resource == null) - throw new PathEngineException("%resource cannot be used in this context"); + throw new PathEngineException("%resource cannot be used in this context", expr.getStart(), expr.toString()); return new TypeDetails(CollectionStatus.SINGLETON, context.resource); } else if (s.equals("%rootResource")) { if (context.resource == null) - throw new PathEngineException("%rootResource cannot be used in this context"); + throw new PathEngineException("%rootResource cannot be used in this context", expr.getStart(), expr.toString()); return new TypeDetails(CollectionStatus.SINGLETON, context.resource); } else if (s.equals("%context")) { return context.context; @@ -2319,7 +2392,7 @@ public class FHIRPathEngine { else if (s.startsWith("%`ext-")) return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); else if (hostServices == null) - throw new PathEngineException("Unknown fixed constant type for '"+s+"'"); + throw new PathEngineException("Unknown fixed constant type for '"+s+"'", expr.getStart(), expr.toString()); else return hostServices.resolveConstantType(context.appInfo, s); } @@ -2350,9 +2423,9 @@ public class FHIRPathEngine { return result; } - private TypeDetails executeContextType(ExecutionTypeContext context, String name) throws PathEngineException, DefinitionException { + private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr) throws PathEngineException, DefinitionException { if (hostServices == null) - throw new PathEngineException("Unable to resolve context reference since no host services are provided"); + throw new PathEngineException("Unable to resolve context reference since no host services are provided", expr.getStart(), expr.toString()); return hostServices.resolveConstantType(context.appInfo, name); } @@ -2360,7 +2433,7 @@ public class FHIRPathEngine { if (atEntry && Character.isUpperCase(exp.getName().charAt(0)) && hashTail(type).equals(exp.getName())) // special case for start up return new TypeDetails(CollectionStatus.SINGLETON, type); TypeDetails result = new TypeDetails(null); - getChildTypesByName(type, exp.getName(), result); + getChildTypesByName(type, exp.getName(), result, exp); return result; } @@ -2390,11 +2463,11 @@ public class FHIRPathEngine { case Exists : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case SubsetOf : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, focus); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case SupersetOf : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, focus); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case IsDistinct : @@ -2414,16 +2487,16 @@ public class FHIRPathEngine { case Aggregate : return anything(focus.getCollectionStatus()); case Item : { - checkOrdered(focus, "item"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "item", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case As : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); } case OfType : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); } case Type : { @@ -2441,31 +2514,31 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_ClassInfo); } case Is : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Single : return focus.toSingleton(); case First : { - checkOrdered(focus, "first"); + checkOrdered(focus, "first", exp); return focus.toSingleton(); } case Last : { - checkOrdered(focus, "last"); + checkOrdered(focus, "last", exp); return focus.toSingleton(); } case Tail : { - checkOrdered(focus, "tail"); + checkOrdered(focus, "tail", exp); return focus; } case Skip : { - checkOrdered(focus, "skip"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "skip", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case Take : { - checkOrdered(focus, "take"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "take", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case Union : { @@ -2488,76 +2561,76 @@ public class FHIRPathEngine { return types; } case Lower : { - checkContextString(focus, "lower"); + checkContextString(focus, "lower", exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Upper : { - checkContextString(focus, "upper"); + checkContextString(focus, "upper", exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case ToChars : { - checkContextString(focus, "toChars"); + checkContextString(focus, "toChars", exp); return new TypeDetails(CollectionStatus.ORDERED, TypeDetails.FP_String); } case IndexOf : { - checkContextString(focus, "indexOf"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "indexOf", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Substring : { - checkContextString(focus, "subString"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkContextString(focus, "subString", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case StartsWith : { - checkContextString(focus, "startsWith"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "startsWith", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case EndsWith : { - checkContextString(focus, "endsWith"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "endsWith", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Matches : { - checkContextString(focus, "matches"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "matches", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ReplaceMatches : { - checkContextString(focus, "replaceMatches"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "replaceMatches", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Contains : { - checkContextString(focus, "contains"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "contains", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Replace : { - checkContextString(focus, "replace"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, "string"), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "replace", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, "string"), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Length : { - checkContextPrimitive(focus, "length", false); + checkContextPrimitive(focus, "length", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Children : - return childTypes(focus, "*"); + return childTypes(focus, "*", exp); case Descendants : - return childTypes(focus, "**"); + return childTypes(focus, "**", exp); case MemberOf : { - checkContextCoded(focus, "memberOf"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextCoded(focus, "memberOf", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Trace : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; } case Check : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; } case Today : @@ -2565,11 +2638,11 @@ public class FHIRPathEngine { case Now : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); case Resolve : { - checkContextReference(focus, "resolve"); + checkContextReference(focus, "resolve", exp); return new TypeDetails(CollectionStatus.SINGLETON, "DomainResource"); } case Extension : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, "Extension"); } case AnyTrue: @@ -2585,42 +2658,42 @@ public class FHIRPathEngine { case HtmlChecks : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Alias : - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return anything(CollectionStatus.SINGLETON); case AliasAs : - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; case ToInteger : { - checkContextPrimitive(focus, "toInteger", true); + checkContextPrimitive(focus, "toInteger", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case ToDecimal : { - checkContextPrimitive(focus, "toDecimal", true); + checkContextPrimitive(focus, "toDecimal", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } case ToString : { - checkContextPrimitive(focus, "toString", true); + checkContextPrimitive(focus, "toString", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case ToQuantity : { - checkContextPrimitive(focus, "toQuantity", true); + checkContextPrimitive(focus, "toQuantity", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); } case ToBoolean : { - checkContextPrimitive(focus, "toBoolean", false); + checkContextPrimitive(focus, "toBoolean", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ToDateTime : { - checkContextPrimitive(focus, "toBoolean", false); + checkContextPrimitive(focus, "toBoolean", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); } case ToTime : { - checkContextPrimitive(focus, "toBoolean", false); + checkContextPrimitive(focus, "toBoolean", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); } case ConvertsToString : case ConvertsToQuantity :{ - checkContextPrimitive(focus, exp.getFunction().toCode(), true); + checkContextPrimitive(focus, exp.getFunction().toCode(), true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ConvertsToInteger : @@ -2628,11 +2701,11 @@ public class FHIRPathEngine { case ConvertsToDateTime : case ConvertsToTime : case ConvertsToBoolean : { - checkContextPrimitive(focus, exp.getFunction().toCode(), false); + checkContextPrimitive(focus, exp.getFunction().toCode(), false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ConformsTo: { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Custom : { @@ -2645,7 +2718,7 @@ public class FHIRPathEngine { } - private void checkParamTypes(String funcName, List paramTypes, TypeDetails... typeSet) throws PathEngineException { + private void checkParamTypes(ExpressionNode expr, String funcName, List paramTypes, TypeDetails... typeSet) throws PathEngineException { int i = 0; for (TypeDetails pt : typeSet) { if (i == paramTypes.size()) @@ -2654,47 +2727,47 @@ public class FHIRPathEngine { i++; for (String a : actual.getTypes()) { if (!pt.hasType(worker, a)) - throw new PathEngineException("The parameter type '"+a+"' is not legal for "+funcName+" parameter "+Integer.toString(i)+". expecting "+pt.toString()); + throw new PathEngineException("The parameter type '"+a+"' is not legal for "+funcName+" parameter "+Integer.toString(i)+". expecting "+pt.toString(), expr.getStart(), expr.toString()); } } } - private void checkOrdered(TypeDetails focus, String name) throws PathEngineException { + private void checkOrdered(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (focus.getCollectionStatus() == CollectionStatus.UNORDERED) - throw new PathEngineException("The function '"+name+"'() can only be used on ordered collections"); + throw new PathEngineException("The function '"+name+"'() can only be used on ordered collections", expr.getStart(), expr.toString()); } - private void checkContextReference(TypeDetails focus, String name) throws PathEngineException { + private void checkContextReference(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "Reference") && !focus.hasType(worker, "canonical")) - throw new PathEngineException("The function '"+name+"'() can only be used on string, uri, canonical, Reference"); + throw new PathEngineException("The function '"+name+"'() can only be used on string, uri, canonical, Reference", expr.getStart(), expr.toString()); } - private void checkContextCoded(TypeDetails focus, String name) throws PathEngineException { + private void checkContextCoded(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "Coding") && !focus.hasType(worker, "CodeableConcept")) - throw new PathEngineException("The function '"+name+"'() can only be used on string, code, uri, Coding, CodeableConcept"); + throw new PathEngineException("The function '"+name+"'() can only be used on string, code, uri, Coding, CodeableConcept", expr.getStart(), expr.toString()); } - private void checkContextString(TypeDetails focus, String name) throws PathEngineException { + private void checkContextString(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "canonical") && !focus.hasType(worker, "id")) - throw new PathEngineException("The function '"+name+"'() can only be used on string, uri, code, id, but found "+focus.describe()); + throw new PathEngineException("The function '"+name+"'() can only be used on string, uri, code, id, but found "+focus.describe(), expr.getStart(), expr.toString()); } - private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty) throws PathEngineException { + private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty, ExpressionNode expr) throws PathEngineException { if (canQty) { if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) - throw new PathEngineException("The function '"+name+"'() can only be used on a Quantity or on "+primitiveTypes.toString()); + throw new PathEngineException("The function '"+name+"'() can only be used on a Quantity or on "+primitiveTypes.toString(), expr.getStart(), expr.toString()); } else if (!focus.hasType(primitiveTypes)) - throw new PathEngineException("The function '"+name+"'() can only be used on "+primitiveTypes.toString()); + throw new PathEngineException("The function '"+name+"'() can only be used on "+primitiveTypes.toString(), expr.getStart(), expr.toString()); } - private TypeDetails childTypes(TypeDetails focus, String mask) throws PathEngineException, DefinitionException { + private TypeDetails childTypes(TypeDetails focus, String mask, ExpressionNode expr) throws PathEngineException, DefinitionException { TypeDetails result = new TypeDetails(CollectionStatus.UNORDERED); for (String f : focus.getTypes()) - getChildTypesByName(f, mask, result); + getChildTypesByName(f, mask, result, expr); return result; } @@ -2823,7 +2896,7 @@ public class FHIRPathEngine { for (Base item : focus) { pc.clear(); pc.add(item); - Equality eq = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true)); + Equality eq = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); if (eq != Equality.True) { all = false; break; @@ -2918,15 +2991,15 @@ public class FHIRPathEngine { result.add(new StringType(n)); } else { - throw new PathEngineException(String.format("funcReplace() : checking for 2 arguments (pattern, substitution) but found %d items", exp.getParameters().size())); + throw new PathEngineException(String.format("funcReplace() : checking for 2 arguments (pattern, substitution) but found %d items", exp.getParameters().size()), exp.getStart(), exp.toString()); } } else { - throw new PathEngineException(String.format("funcReplace() : checking for 1 string item but found empty item")); + throw new PathEngineException(String.format("funcReplace() : checking for 1 string item but found empty item"), exp.getStart(), exp.toString()); } } else { - throw new PathEngineException(String.format("funcReplace() : checking for 1 string item but found %d items", focus.size())); + throw new PathEngineException(String.format("funcReplace() : checking for 1 string item but found %d items", focus.size()), exp.getStart(), exp.toString()); } return result; } @@ -3043,7 +3116,7 @@ public class FHIRPathEngine { private List funcIif(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List n1 = execute(context, focus, exp.getParameters().get(0), true); - Equality v = asBool(n1); + Equality v = asBool(n1, exp); if (v == Equality.True) return execute(context, focus, exp.getParameters().get(1), true); @@ -3115,7 +3188,7 @@ public class FHIRPathEngine { private List funcSingle(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { if (focus.size() == 1) return focus; - throw new PathEngineException(String.format("Single() : checking for 1 item but found %d items", focus.size())); + throw new PathEngineException(String.format("Single() : checking for 1 item but found %d items", focus.size()), exp.getStart(), exp.toString()); } @@ -3127,10 +3200,10 @@ public class FHIRPathEngine { ExpressionNode texp = exp.getParameters().get(0); if (texp.getKind() != Kind.Name) - throw new PathEngineException("Unsupported Expression type for Parameter on Is"); + throw new PathEngineException("Unsupported Expression type for Parameter on Is", exp.getStart(), exp.toString()); if (texp.getInner() != null) { if (texp.getInner().getKind() != Kind.Name) - throw new PathEngineException("Unsupported Expression type for Parameter on Is"); + throw new PathEngineException("Unsupported Expression type for Parameter on Is", exp.getStart(), exp.toString()); ns = texp.getName(); n = texp.getInner().getName(); } else if (Utilities.existsInList(texp.getName(), "Boolean", "Integer", "Decimal", "String", "DateTime", "Time", "SimpleTypeInfo", "ClassInfo")) { @@ -3375,7 +3448,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v != Equality.False) { all = false; break; @@ -3405,7 +3478,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v == Equality.False) { any = true; break; @@ -3435,7 +3508,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v != Equality.True) { all = false; break; @@ -3465,7 +3538,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v == Equality.True) { any = true; break; @@ -3889,7 +3962,7 @@ public class FHIRPathEngine { for (Base item : focus) { pc.clear(); pc.add(item); - Equality v = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true)); + Equality v = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); if (v == Equality.True) result.add(item); } @@ -3924,7 +3997,7 @@ public class FHIRPathEngine { private List funcNot(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { List result = new ArrayList(); - Equality v = asBool(focus); + Equality v = asBool(focus, exp); if (v != Equality.Null) result.add(new BooleanType(v != Equality.True)); return result; @@ -3947,9 +4020,9 @@ public class FHIRPathEngine { } - private void getChildTypesByName(String type, String name, TypeDetails result) throws PathEngineException, DefinitionException { + private void getChildTypesByName(String type, String name, TypeDetails result, ExpressionNode expr) throws PathEngineException, DefinitionException { if (Utilities.noString(type)) - throw new PathEngineException("No type provided in BuildToolPathEvaluator.getChildTypesByName"); + throw new PathEngineException("No type provided in BuildToolPathEvaluator.getChildTypesByName", expr.getStart(), expr.toString()); if (type.equals("http://hl7.org/fhir/StructureDefinition/xhtml")) return; if (type.startsWith(Constants.NS_SYSTEM_TYPE)) @@ -3973,7 +4046,7 @@ public class FHIRPathEngine { List sdl = new ArrayList(); ElementDefinitionMatch m = null; if (type.contains("#")) - m = getElementDefinition(sd, type.substring(type.indexOf("#")+1), false); + m = getElementDefinition(sd, type.substring(type.indexOf("#")+1), false, expr); if (m != null && hasDataType(m.definition)) { if (m.fixedType != null) { @@ -4012,11 +4085,11 @@ public class FHIRPathEngine { if (t.getCode().equals("Resource")) { for (String rn : worker.getResourceNames()) { if (!result.hasType(worker, rn)) { - getChildTypesByName(result.addType(rn), "**", result); + getChildTypesByName(result.addType(rn), "**", result, expr); } } } else if (!result.hasType(worker, tn)) { - getChildTypesByName(result.addType(tn), "**", result); + getChildTypesByName(result.addType(tn), "**", result, expr); } } } @@ -4039,7 +4112,7 @@ public class FHIRPathEngine { } else { path = sdi.getSnapshot().getElement().get(0).getPath()+tail+"."+name; - ElementDefinitionMatch ed = getElementDefinition(sdi, path, false); + ElementDefinitionMatch ed = getElementDefinition(sdi, path, false, expr); if (ed != null) { if (!Utilities.noString(ed.getFixedType())) result.addType(ed.getFixedType()); @@ -4088,7 +4161,7 @@ public class FHIRPathEngine { } - private ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName) throws PathEngineException { + private ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName, ExpressionNode expr) throws PathEngineException { for (ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().equals(path)) { if (ed.hasContentReference()) { @@ -4108,15 +4181,15 @@ public class FHIRPathEngine { if (ed.getPath().contains(".") && path.startsWith(ed.getPath()+".") && (ed.getType().size() > 0) && !isAbstractType(ed.getType())) { // now we walk into the type. if (ed.getType().size() > 1) // if there's more than one type, the test above would fail this - throw new PathEngineException("Internal typing issue...."); + throw new PathEngineException("Internal typing issue....", expr.getStart(), expr.toString()); StructureDefinition nsd = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(ed.getType().get(0).getCode(), worker.getOverrideVersionNs())); if (nsd == null) - throw new PathEngineException("Unknown type "+ed.getType().get(0).getCode()); - return getElementDefinition(nsd, nsd.getId()+path.substring(ed.getPath().length()), allowTypedName); + throw new PathEngineException("Unknown type "+ed.getType().get(0).getCode(), expr.getStart(), expr.toString()); + return getElementDefinition(nsd, nsd.getId()+path.substring(ed.getPath().length()), allowTypedName, expr); } if (ed.hasContentReference() && path.startsWith(ed.getPath()+".")) { ElementDefinitionMatch m = getElementDefinitionById(sd, ed.getContentReference()); - return getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName); + return getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName, expr); } } return null; @@ -4291,13 +4364,13 @@ public class FHIRPathEngine { return path.substring(path.lastIndexOf(".") + 1); } - private Equality asBool(List items) throws PathEngineException { + private Equality asBool(List items, ExpressionNode expr) throws PathEngineException { if (items.size() == 0) return Equality.Null; else if (items.size() == 1) return asBool(items.get(0)); else - throw new PathEngineException("Unable to evaluate as a boolean: "+convertToString(items)); + throw new PathEngineException("Unable to evaluate as a boolean: "+convertToString(items), expr.getStart(), expr.toString()); } private Equality asBoolFromInt(String s) { diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/NPMPackageGenerator.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/NPMPackageGenerator.java index 16577c02a..9b6ce7b4f 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/NPMPackageGenerator.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/NPMPackageGenerator.java @@ -58,8 +58,8 @@ import org.hl7.fhir.r4.model.ImplementationGuide.ImplementationGuideDependsOnCom import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.PackageGenerator.PackageType; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.ToolsVersion; +import org.hl7.fhir.utilities.npm.PackageGenerator.PackageType; import com.google.gson.Gson; import com.google.gson.GsonBuilder; diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/CDARoundTripTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/CDARoundTripTests.java index ca1be1254..921c4bbba 100644 --- a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/CDARoundTripTests.java +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/CDARoundTripTests.java @@ -8,8 +8,8 @@ import org.hl7.fhir.r4.elementmodel.Element; import org.hl7.fhir.r4.elementmodel.Manager; import org.hl7.fhir.r4.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r4.formats.IParser.OutputStyle; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java index 0b7f7b0f2..79ea4fcb0 100644 --- a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/FHIRPathTests.java @@ -205,7 +205,7 @@ public class FHIRPathTests { Assertions.assertTrue(outcome.get(i).equalsDeep(q), String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString())); } else { Assertions.assertTrue(outcome.get(i) instanceof PrimitiveType, String.format("Outcome %d: Value should be a primitive type but was %s", i, outcome.get(i).fhirType())); - Assertions.assertEquals(v, ((PrimitiveType) outcome.get(i)).asStringValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression)); + Assertions.assertEquals(v, ((PrimitiveType) outcome.get(i)).fpValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, ((PrimitiveType) outcome.get(i)).fpValue(), expression)); } } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java index 289f7b52d..d9893d099 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java @@ -220,6 +220,7 @@ public class CanonicalResourceManager { if (enforceUniqueId && map.containsKey(cr.getId())) { drop(cr.getId()); } + // special case logic for UTG support prior to version 5 if (cr.getPackageInfo() != null && cr.getPackageInfo().getId().startsWith("hl7.terminology")) { List> toDrop = new ArrayList<>(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index 8a0593606..72b79b4bd 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -67,8 +67,8 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.IResourceValidator; import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.TranslationServices; -import org.hl7.fhir.utilities.cache.BasePackageCacheManager; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.BasePackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationOptions; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index 207d6ec29..87b8571f7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -81,11 +81,11 @@ import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.BasePackageCacheManager; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.NpmPackage.PackageResourceInformation; import org.hl7.fhir.utilities.i18n.I18nConstants; +import org.hl7.fhir.utilities.npm.BasePackageCacheManager; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BaseDateTimeType.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BaseDateTimeType.java index 6f68f819d..b9dcb4ace 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BaseDateTimeType.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BaseDateTimeType.java @@ -1006,4 +1006,9 @@ public abstract class BaseDateTimeType extends PrimitiveType { } } + @Override + public String fpValue() { + return "@"+primitiveValue(); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java index 3ef89fd7b..253a39b65 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java @@ -34,6 +34,7 @@ package org.hl7.fhir.r5.model; import java.util.ArrayList; import java.util.List; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; public class ExpressionNode { @@ -41,31 +42,7 @@ public class ExpressionNode { public enum Kind { Name, Function, Constant, Group, Unary } - public static class SourceLocation { - private int line; - private int column; - public SourceLocation(int line, int column) { - super(); - this.line = line; - this.column = column; - } - public int getLine() { - return line; - } - public int getColumn() { - return column; - } - public void setLine(int line) { - this.line = line; - } - public void setColumn(int column) { - this.column = column; - } - public String toString() { - return Integer.toString(line)+", "+Integer.toString(column); - } - } public enum Function { Custom, diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/PrimitiveType.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/PrimitiveType.java index b42d3d04e..49ac41018 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/PrimitiveType.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/PrimitiveType.java @@ -248,4 +248,8 @@ public abstract class PrimitiveType extends DataType implements IPrimitiveTyp public boolean hasPrimitiveValue() { return StringUtils.isNotBlank(getValueAsString()); } + + public String fpValue() { + return primitiveValue(); + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java index 85b238893..94ff7037f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java @@ -234,6 +234,7 @@ public class ValueSetRenderer extends TerminologyRenderer { } if (doDefinition) { tr.td().b().tx("Definition"); + doLangs = false; } else { // if we're not doing definitions and we don't have too many languages, we'll do them in line if (langs.size() < MAX_LANGS_IN_LINE) { @@ -260,9 +261,9 @@ public class ValueSetRenderer extends TerminologyRenderer { x.para().b().tx("Additional Language Displays"); t = x.table( "codes"); tr = t.tr(); - tdDisp.b().tx("Code"); + tr.td().b().tx("Code"); for (String lang : langs) { - tdDisp.b().addText(describeLang(lang)); + tr.td().b().addText(describeLang(lang)); } for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { addLanguageRow(c, t, langs); @@ -482,7 +483,6 @@ public class ValueSetRenderer extends TerminologyRenderer { if ("1.0m".equals(v)) return "Jul 1999"; if ("1.0l".equals(v)) return "Jan 1998"; if ("1.0ja".equals(v)) return "Oct 1997"; - return null; } @@ -662,7 +662,9 @@ public class ValueSetRenderer extends TerminologyRenderer { td.i().tx("("+mapping.comp.getComment()+")"); } } - addLangaugesToRow(c, langs, tr); + if (doLangs) { + addLangaugesToRow(c, langs, tr); + } for (ValueSetExpansionContainsComponent cc : c.getContains()) { addExpansionRowToTable(t, cc, i+1, doLevel, doSystem, doDefinition, maps, allCS, langs, doLangs); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java index c7fb031c0..071fc44a9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java @@ -22,8 +22,8 @@ import org.hl7.fhir.utilities.CSFile; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.hl7.fhir.utilities.tests.BaseTestingUtilities; import org.w3c.dom.Document; import org.w3c.dom.Element; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java index e08acbefb..4400bcdc8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java @@ -34,7 +34,7 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.model.ExpressionNode; -import org.hl7.fhir.r5.model.ExpressionNode.SourceLocation; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; // shared lexer for concrete syntaxes diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index 5b6c553ff..334b8648f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -5,6 +5,7 @@ import java.math.RoundingMode; import java.rmi.server.LoaderHandler; import java.util.ArrayList; import java.util.Base64; +import java.util.Calendar; import java.util.Date; import java.util.EnumSet; import java.util.HashMap; @@ -38,7 +39,6 @@ import org.hl7.fhir.r5.model.ExpressionNode.CollectionStatus; import org.hl7.fhir.r5.model.ExpressionNode.Function; import org.hl7.fhir.r5.model.ExpressionNode.Kind; import org.hl7.fhir.r5.model.ExpressionNode.Operation; -import org.hl7.fhir.r5.model.ExpressionNode.SourceLocation; import org.hl7.fhir.r5.model.Property.PropertyMatcher; import org.hl7.fhir.r5.model.IntegerType; import org.hl7.fhir.r5.model.Property; @@ -58,6 +58,7 @@ import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MergedList; import org.hl7.fhir.utilities.MergedList.MergeNode; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -509,11 +510,11 @@ public class FHIRPathEngine { } StructureDefinition sd = worker.fetchResource(StructureDefinition.class, ctxt); if (sd == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, context); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, context); } - ElementDefinitionMatch ed = getElementDefinition(sd, context, true); + ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); if (ed == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); } if (ed.fixedType != null) { types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); @@ -530,12 +531,16 @@ public class FHIRPathEngine { return executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, true); } - private FHIRException makeException(String constName, Object... args) { + private FHIRException makeException(ExpressionNode holder, String constName, Object... args) { String fmt = worker.formatMessage(constName, args); if (location != null) { fmt = fmt + " "+worker.formatMessage(I18nConstants.FHIRPATH_LOCATION, location); } - return new PathEngineException(fmt); + if (holder != null) { + return new PathEngineException(fmt, holder.getStart(), holder.toString()); + } else { + return new PathEngineException(fmt); + } } public TypeDetails check(Object appContext, StructureDefinition sd, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException { @@ -544,9 +549,9 @@ public class FHIRPathEngine { if (!context.contains(".")) { types = new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()); } else { - ElementDefinitionMatch ed = getElementDefinition(sd, context, true); + ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); if (ed == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); } if (ed.fixedType != null) { types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); @@ -907,7 +912,7 @@ public class FHIRPathEngine { aliases = new HashMap(aliases); // clone it, since it's going to change } if (focus.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_ALIAS_COLLECTION); + throw makeException(null, I18nConstants.FHIRPATH_ALIAS_COLLECTION); } aliases.put(name, focus.size() == 0 ? null : focus.get(0)); } @@ -956,6 +961,7 @@ public class FHIRPathEngine { wrapper = new ExpressionNode(lexer.nextId()); wrapper.setKind(Kind.Unary); wrapper.setOperation(ExpressionNode.Operation.fromCode(lexer.take())); + wrapper.setStart(lexer.getCurrentLocation()); wrapper.setProximal(proximal); } @@ -967,6 +973,7 @@ public class FHIRPathEngine { wrapper.setKind(Kind.Unary); wrapper.setOperation(ExpressionNode.Operation.fromCode(lexer.getCurrent().substring(0, 1))); wrapper.setProximal(proximal); + wrapper.setStart(lexer.getCurrentLocation()); lexer.setCurrent(lexer.getCurrent().substring(1)); } result.setConstant(processConstant(lexer)); @@ -1354,7 +1361,7 @@ public class FHIRPathEngine { work.addAll(work2); break; case Constant: - Base b = resolveConstant(context, exp.getConstant(), false); + Base b = resolveConstant(context, exp.getConstant(), false, exp); if (b != null) { work.add(b); } @@ -1372,16 +1379,16 @@ public class FHIRPathEngine { ExpressionNode next = exp.getOpNext(); ExpressionNode last = exp; while (next != null) { - List work2 = preOperate(work, last.getOperation()); + List work2 = preOperate(work, last.getOperation(), exp); if (work2 != null) { work = work2; } else if (last.getOperation() == Operation.Is || last.getOperation() == Operation.As) { work2 = executeTypeName(context, focus, next, false); - work = operate(context, work, last.getOperation(), work2); + work = operate(context, work, last.getOperation(), work2, last); } else { work2 = execute(context, focus, next, true); - work = operate(context, work, last.getOperation(), work2); + work = operate(context, work, last.getOperation(), work2, last); // System.out.println("Result of {'"+last.toString()+" "+last.getOperation().toCode()+" "+next.toString()+"'}: "+focus.toString()); } last = next; @@ -1403,7 +1410,7 @@ public class FHIRPathEngine { } - private List preOperate(List left, Operation operation) throws PathEngineException { + private List preOperate(List left, Operation operation, ExpressionNode expr) throws PathEngineException { if (left.size() == 0) { return null; } @@ -1413,7 +1420,7 @@ public class FHIRPathEngine { case Or: return isBoolean(left, true) ? makeBoolean(true) : null; case Implies: - Equality v = asBool(left); + Equality v = asBool(left, expr); return v == Equality.False ? makeBoolean(true) : null; default: return null; @@ -1444,13 +1451,13 @@ public class FHIRPathEngine { } else if (atEntry && exp.getName().equals("$total")) { result.update(anything(CollectionStatus.UNORDERED)); } else if (atEntry && focus == null) { - result.update(executeContextType(context, exp.getName())); + result.update(executeContextType(context, exp.getName(), exp)); } else { for (String s : focus.getTypes()) { result.update(executeType(s, exp, atEntry)); } if (result.hasNoTypes()) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_NAME, exp.getName(), focus.describe()); + throw makeException(exp, I18nConstants.FHIRPATH_UNKNOWN_NAME, exp.getName(), focus.describe()); } } break; @@ -1463,7 +1470,7 @@ public class FHIRPathEngine { result.addType(TypeDetails.FP_Quantity); break; case Constant: - result.update(resolveConstantType(context, exp.getConstant())); + result.update(resolveConstantType(context, exp.getConstant(), exp)); break; case Group: result.update(executeType(context, focus, exp.getGroup(), atEntry)); @@ -1484,7 +1491,7 @@ public class FHIRPathEngine { } else { work = executeType(context, focus, next, atEntry); } - result = operateTypes(result, last.getOperation(), work); + result = operateTypes(result, last.getOperation(), work, last); last = next; next = next.getOpNext(); } @@ -1493,21 +1500,21 @@ public class FHIRPathEngine { return result; } - private Base resolveConstant(ExecutionContext context, Base constant, boolean beforeContext) throws PathEngineException { + private Base resolveConstant(ExecutionContext context, Base constant, boolean beforeContext, ExpressionNode expr) throws PathEngineException { if (!(constant instanceof FHIRConstant)) { return constant; } FHIRConstant c = (FHIRConstant) constant; if (c.getValue().startsWith("%")) { - return resolveConstant(context, c.getValue(), beforeContext); + return resolveConstant(context, c.getValue(), beforeContext, expr); } else if (c.getValue().startsWith("@")) { - return processDateConstant(context.appInfo, c.getValue().substring(1)); + return processDateConstant(context.appInfo, c.getValue().substring(1), expr); } else { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, c.getValue()); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, c.getValue()); } } - private Base processDateConstant(Object appInfo, String value) throws PathEngineException { + private Base processDateConstant(Object appInfo, String value, ExpressionNode expr) throws PathEngineException { String date = null; String time = null; String tz = null; @@ -1555,7 +1562,7 @@ public class FHIRPathEngine { if (date == null) { if (tz != null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, value); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, value); } else { TimeType tt = new TimeType(time); tt.setPrecision(temp); @@ -1571,7 +1578,7 @@ public class FHIRPathEngine { } - private Base resolveConstant(ExecutionContext context, String s, boolean beforeContext) throws PathEngineException { + private Base resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr) throws PathEngineException { if (s.equals("%sct")) { return new StringType("http://snomed.info/sct").noExtensions(); } else if (s.equals("%loinc")) { @@ -1580,12 +1587,12 @@ public class FHIRPathEngine { return new StringType("http://unitsofmeasure.org").noExtensions(); } else if (s.equals("%resource")) { if (context.focusResource == null) { - throw makeException(I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); + throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); } return context.focusResource; } else if (s.equals("%rootResource")) { if (context.rootResource == null) { - throw makeException(I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); + throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); } return context.rootResource; } else if (s.equals("%context")) { @@ -1599,7 +1606,7 @@ public class FHIRPathEngine { } else if (s.startsWith("%`ext-")) { return new StringType("http://hl7.org/fhir/StructureDefinition/"+s.substring(6, s.length()-1)).noExtensions(); } else if (hostServices == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { return hostServices.resolveConstant(context.appInfo, s.substring(1), beforeContext); } @@ -1660,39 +1667,39 @@ public class FHIRPathEngine { } - private List operate(ExecutionContext context, List left, Operation operation, List right) throws FHIRException { + private List operate(ExecutionContext context, List left, Operation operation, List right, ExpressionNode holder) throws FHIRException { switch (operation) { - case Equals: return opEquals(left, right); - case Equivalent: return opEquivalent(left, right); - case NotEquals: return opNotEquals(left, right); - case NotEquivalent: return opNotEquivalent(left, right); - case LessThan: return opLessThan(left, right); - case Greater: return opGreater(left, right); - case LessOrEqual: return opLessOrEqual(left, right); - case GreaterOrEqual: return opGreaterOrEqual(left, right); - case Union: return opUnion(left, right); - case In: return opIn(left, right); - case MemberOf: return opMemberOf(context, left, right); - case Contains: return opContains(left, right); - case Or: return opOr(left, right); - case And: return opAnd(left, right); - case Xor: return opXor(left, right); - case Implies: return opImplies(left, right); - case Plus: return opPlus(left, right); - case Times: return opTimes(left, right); - case Minus: return opMinus(left, right); - case Concatenate: return opConcatenate(left, right); - case DivideBy: return opDivideBy(left, right); - case Div: return opDiv(left, right); - case Mod: return opMod(left, right); - case Is: return opIs(left, right); - case As: return opAs(left, right); + case Equals: return opEquals(left, right, holder); + case Equivalent: return opEquivalent(left, right, holder); + case NotEquals: return opNotEquals(left, right, holder); + case NotEquivalent: return opNotEquivalent(left, right, holder); + case LessThan: return opLessThan(left, right, holder); + case Greater: return opGreater(left, right, holder); + case LessOrEqual: return opLessOrEqual(left, right, holder); + case GreaterOrEqual: return opGreaterOrEqual(left, right, holder); + case Union: return opUnion(left, right, holder); + case In: return opIn(left, right, holder); + case MemberOf: return opMemberOf(context, left, right, holder); + case Contains: return opContains(left, right, holder); + case Or: return opOr(left, right, holder); + case And: return opAnd(left, right, holder); + case Xor: return opXor(left, right, holder); + case Implies: return opImplies(left, right, holder); + case Plus: return opPlus(left, right, holder); + case Times: return opTimes(left, right, holder); + case Minus: return opMinus(left, right, holder); + case Concatenate: return opConcatenate(left, right, holder); + case DivideBy: return opDivideBy(left, right, holder); + case Div: return opDiv(left, right, holder); + case Mod: return opMod(left, right, holder); + case Is: return opIs(left, right, holder); + case As: return opAs(left, right, holder); default: throw new Error("Not Done Yet: "+operation.toCode()); } } - private List opAs(List left, List right) { + private List opAs(List left, List right, ExpressionNode expr) { List result = new ArrayList<>(); if (right.size() != 1) { return result; @@ -1708,7 +1715,7 @@ public class FHIRPathEngine { } - private List opIs(List left, List right) { + private List opIs(List left, List right, ExpressionNode expr) { List result = new ArrayList(); if (left.size() == 0 || right.size() == 0) { } else if (left.size() != 1 || right.size() != 1) @@ -1727,7 +1734,7 @@ public class FHIRPathEngine { } - private TypeDetails operateTypes(TypeDetails left, Operation operation, TypeDetails right) { + private TypeDetails operateTypes(TypeDetails left, Operation operation, TypeDetails right, ExpressionNode expr) { switch (operation) { case Equals: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Equivalent: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); @@ -1771,6 +1778,12 @@ public class FHIRPathEngine { result.addType(TypeDetails.FP_Decimal); } else if (left.hasType(worker, "string", "id", "code", "uri") && right.hasType(worker, "string", "id", "code", "uri")) { result.addType(TypeDetails.FP_String); + } else if (left.hasType(worker, "date", "dateTime", "instant")) { + if (right.hasType(worker, "Quantity")) { + result.addType(left.getType()); + } else { + throw new PathEngineException(String.format("Error in date arithmetic: Unable to add type {0} to {1}", right.getType(), left.getType()), expr.getOpStart(), expr.toString()); + } } return result; case Minus: @@ -1781,6 +1794,12 @@ public class FHIRPathEngine { result.addType(TypeDetails.FP_Decimal); } else if (left.hasType(worker, "Quantity") && right.hasType(worker, "Quantity")) { result.addType(TypeDetails.FP_Quantity); + } else if (left.hasType(worker, "date", "dateTime", "instant")) { + if (right.hasType(worker, "Quantity")) { + result.addType(left.getType()); + } else { + throw new PathEngineException(String.format("Error in date arithmetic: Unable to subtract type {0} from {1}", right.getType(), left.getType())); + } } return result; case Div: @@ -1801,7 +1820,7 @@ public class FHIRPathEngine { } - private List opEquals(List left, List right) { + private List opEquals(List left, List right, ExpressionNode expr) { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } @@ -1830,7 +1849,7 @@ public class FHIRPathEngine { } } - private List opNotEquals(List left, List right) { + private List opNotEquals(List left, List right, ExpressionNode expr) { if (!legacyMode && (left.size() == 0 || right.size() == 0)) { return new ArrayList(); } @@ -2061,7 +2080,7 @@ public class FHIRPathEngine { - private List opEquivalent(List left, List right) throws PathEngineException { + private List opEquivalent(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() != right.size()) { return makeBoolean(false); } @@ -2083,7 +2102,7 @@ public class FHIRPathEngine { return makeBoolean(res); } - private List opNotEquivalent(List left, List right) throws PathEngineException { + private List opNotEquivalent(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() != right.size()) { return makeBoolean(true); } @@ -2107,7 +2126,7 @@ public class FHIRPathEngine { private final static String[] FHIR_TYPES_STRING = new String[] {"string", "uri", "code", "oid", "id", "uuid", "sid", "markdown", "base64Binary", "canonical", "url"}; - private List opLessThan(List left, List right) throws FHIRException { + private List opLessThan(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); @@ -2133,13 +2152,13 @@ public class FHIRPathEngine { return makeBoolean(i < 0); } } else { - throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); } } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("code"); List rUnit = right.get(0).listChildrenByName("code"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) { return makeBoolean(false); @@ -2148,14 +2167,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); - return opLessThan(dl, dr); + return opLessThan(dl, dr, expr); } } } return new ArrayList(); } - private List opGreater(List left, List right) throws FHIRException { + private List opGreater(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) return new ArrayList(); if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { @@ -2180,13 +2199,13 @@ public class FHIRPathEngine { return makeBoolean(i > 0); } } else { - throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); } } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("unit"); List rUnit = right.get(0).listChildrenByName("unit"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opGreater(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opGreater(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) { return makeBoolean(false); @@ -2195,14 +2214,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); - return opGreater(dl, dr); + return opGreater(dl, dr, expr); } } } return new ArrayList(); } - private List opLessOrEqual(List left, List right) throws FHIRException { + private List opLessOrEqual(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } @@ -2228,7 +2247,7 @@ public class FHIRPathEngine { return makeBoolean(i <= 0); } } else { - throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); } } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnits = left.get(0).listChildrenByName("unit"); @@ -2236,7 +2255,7 @@ public class FHIRPathEngine { List rUnits = right.get(0).listChildrenByName("unit"); String runit = rUnits.size() == 1 ? rUnits.get(0).primitiveValue() : null; if ((lunit == null && runit == null) || lunit.equals(runit)) { - return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) { return makeBoolean(false); @@ -2245,14 +2264,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); - return opLessOrEqual(dl, dr); + return opLessOrEqual(dl, dr, expr); } } } return new ArrayList(); } - private List opGreaterOrEqual(List left, List right) throws FHIRException { + private List opGreaterOrEqual(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } @@ -2278,13 +2297,13 @@ public class FHIRPathEngine { return makeBoolean(i >= 0); } } else { - throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); } } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { List lUnit = left.get(0).listChildrenByName("unit"); List rUnit = right.get(0).listChildrenByName("unit"); if (Base.compareDeep(lUnit, rUnit, true)) { - return opGreaterOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value")); + return opGreaterOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); } else { if (worker.getUcumService() == null) { return makeBoolean(false); @@ -2293,14 +2312,14 @@ public class FHIRPathEngine { dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); List dr = new ArrayList(); dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); - return opGreaterOrEqual(dl, dr); + return opGreaterOrEqual(dl, dr, expr); } } } return new ArrayList(); } - private List opMemberOf(ExecutionContext context, List left, List right) throws FHIRException { + private List opMemberOf(ExecutionContext context, List left, List right, ExpressionNode expr) throws FHIRException { boolean ans = false; ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, right.get(0).primitiveValue()) : worker.fetchResource(ValueSet.class, right.get(0).primitiveValue()); if (vs != null) { @@ -2325,7 +2344,7 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opIn(List left, List right) throws FHIRException { + private List opIn(List left, List right, ExpressionNode expr) throws FHIRException { if (left.size() == 0) { return new ArrayList(); } @@ -2350,7 +2369,7 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opContains(List left, List right) { + private List opContains(List left, List right, ExpressionNode expr) { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } @@ -2372,21 +2391,21 @@ public class FHIRPathEngine { return makeBoolean(ans); } - private List opPlus(List left, List right) throws PathEngineException { + private List opPlus(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "+"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "+"); } if (!left.get(0).isPrimitive()) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "+", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "+", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "+"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "+"); } - if (!right.get(0).isPrimitive()) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "+", right.get(0).fhirType()); + if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) { + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "+", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2398,27 +2417,82 @@ public class FHIRPathEngine { result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) + Integer.parseInt(r.primitiveValue()))); } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) { result.add(new DecimalType(new BigDecimal(l.primitiveValue()).add(new BigDecimal(r.primitiveValue())))); + } else if (l.isDateTime() && r.hasType("Quantity")) { + result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, false, expr)); } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "+", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "+", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private List opTimes(List left, List right) throws PathEngineException { + private BaseDateTimeType dateAdd(BaseDateTimeType d, Quantity q, boolean negate, ExpressionNode holder) { + BaseDateTimeType result = (BaseDateTimeType) d.copy(); + + int value = negate ? 0 - q.getValue().intValue() : q.getValue().intValue(); + switch (q.hasCode() ? q.getCode() : q.getUnit()) { + case "years": + case "year": + result.add(Calendar.YEAR, value); + break; + case "a": + throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode())); + case "months": + case "month": + result.add(Calendar.MONTH, value); + break; + case "mo": + throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode()), holder.getOpStart(), holder.toString()); + case "weeks": + case "week": + case "wk": + result.add(Calendar.DAY_OF_MONTH, value * 7); + break; + case "days": + case "day": + case "d": + result.add(Calendar.DAY_OF_MONTH, value); + break; + case "hours": + case "hour": + case "h": + result.add(Calendar.HOUR, value); + break; + case "minutes": + case "minute": + case "min": + result.add(Calendar.MINUTE, value); + break; + case "seconds": + case "second": + case "s": + result.add(Calendar.SECOND, value); + break; + case "milliseconds": + case "millisecond": + case "ms": + result.add(Calendar.MILLISECOND, value); + break; + default: + throw new PathEngineException(String.format("Error in date arithmetic: unrecognized time unit %s", q.getCode())); + } + return result; + } + + private List opTimes(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "*"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "*"); } if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "*", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "*", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "*"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "*"); } if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "*", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "*", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2437,27 +2511,27 @@ public class FHIRPathEngine { p = worker.getUcumService().multiply(pl, pr); result.add(pairToQty(p)); } catch (UcumException e) { - throw new PathEngineException(e.getMessage(), e); + throw new PathEngineException(e.getMessage(), expr.getOpStart(), expr.toString(), e); } } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "*", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "*", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private List opConcatenate(List left, List right) throws PathEngineException { + private List opConcatenate(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "&"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "&"); } if (left.size() > 0 && !left.get(0).hasType(FHIR_TYPES_STRING)) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "&", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "&", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "&"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "&"); } if (right.size() > 0 && !right.get(0).hasType(FHIR_TYPES_STRING)) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "&", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "&", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2467,7 +2541,7 @@ public class FHIRPathEngine { return result; } - private List opUnion(List left, List right) { + private List opUnion(List left, List right, ExpressionNode expr) { List result = new ArrayList(); for (Base item : left) { if (!doContains(result, item)) { @@ -2493,9 +2567,9 @@ public class FHIRPathEngine { } - private List opAnd(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opAnd(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case False: return makeBoolean(false); case Null: @@ -2518,9 +2592,9 @@ public class FHIRPathEngine { return list.size() == 1 && list.get(0) instanceof BooleanType && ((BooleanType) list.get(0)).booleanValue() == b; } - private List opOr(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opOr(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case True: return makeBoolean(true); case Null: @@ -2539,9 +2613,9 @@ public class FHIRPathEngine { return makeNull(); } - private List opXor(List left, List right) throws PathEngineException { - Equality l = asBool(left); - Equality r = asBool(right); + private List opXor(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality l = asBool(left, expr); + Equality r = asBool(right, expr); switch (l) { case True: switch (r) { @@ -2561,13 +2635,13 @@ public class FHIRPathEngine { return makeNull(); } - private List opImplies(List left, List right) throws PathEngineException { - Equality eq = asBool(left); + private List opImplies(List left, List right, ExpressionNode expr) throws PathEngineException { + Equality eq = asBool(left, expr); if (eq == Equality.False) { return makeBoolean(true); } else if (right.size() == 0) { return makeNull(); - } else switch (asBool(right)) { + } else switch (asBool(right, expr)) { case False: return eq == Equality.Null ? makeNull() : makeBoolean(false); case Null: return makeNull(); case True: return makeBoolean(true); @@ -2576,21 +2650,21 @@ public class FHIRPathEngine { } - private List opMinus(List left, List right) throws PathEngineException { + private List opMinus(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "-"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "-"); } if (!left.get(0).isPrimitive() && !left.get(0).hasType("Quantity")) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "-", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "-", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "-"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "-"); } - if (!right.get(0).isPrimitive() && !right.get(0).hasType("Quantity")) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "-", right.get(0).fhirType()); + if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) { + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "-", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2607,27 +2681,29 @@ public class FHIRPathEngine { Quantity qty = (Quantity) r; result.add(qty.copy().setValue(qty.getValue().abs())); } + } else if (l.isDateTime() && r.hasType("Quantity")) { + result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, true, expr)); } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "-", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "-", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private List opDivideBy(List left, List right) throws PathEngineException { + private List opDivideBy(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "/"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "/"); } if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "/", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "/", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "/"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "/"); } if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "/", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "/", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2654,26 +2730,26 @@ public class FHIRPathEngine { // just return nothing } } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "/", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "/", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private List opDiv(List left, List right) throws PathEngineException { + private List opDiv(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "div"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "div"); } if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "div", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "div", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "div"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "div"); } if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "div", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "div", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2695,25 +2771,25 @@ public class FHIRPathEngine { // just return nothing } } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "div", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "div", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private List opMod(List left, List right) throws PathEngineException { + private List opMod(List left, List right, ExpressionNode expr) throws PathEngineException { if (left.size() == 0 || right.size() == 0) { return new ArrayList(); } if (left.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "mod"); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "mod"); } if (!left.get(0).isPrimitive()) { - throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "mod", left.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "mod", left.get(0).fhirType()); } if (right.size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "mod"); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "mod"); } if (!right.get(0).isPrimitive()) { - throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "mod", right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "mod", right.get(0).fhirType()); } List result = new ArrayList(); @@ -2735,13 +2811,13 @@ public class FHIRPathEngine { throw new PathEngineException(e); } } else { - throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "mod", left.get(0).fhirType(), right.get(0).fhirType()); + throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "mod", left.get(0).fhirType(), right.get(0).fhirType()); } return result; } - private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr) throws PathEngineException { if (constant instanceof BooleanType) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } else if (constant instanceof IntegerType) { @@ -2751,13 +2827,13 @@ public class FHIRPathEngine { } else if (constant instanceof Quantity) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); } else if (constant instanceof FHIRConstant) { - return resolveConstantType(context, ((FHIRConstant) constant).getValue()); + return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr); } else { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } } - private TypeDetails resolveConstantType(ExecutionTypeContext context, String s) throws PathEngineException { + private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr) throws PathEngineException { if (s.startsWith("@")) { if (s.startsWith("@T")) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); @@ -2772,12 +2848,12 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } else if (s.equals("%resource")) { if (context.resource == null) { - throw makeException(I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); + throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); } return new TypeDetails(CollectionStatus.SINGLETON, context.resource); } else if (s.equals("%rootResource")) { if (context.resource == null) { - throw makeException(I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); + throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); } return new TypeDetails(CollectionStatus.SINGLETON, context.resource); } else if (s.equals("%context")) { @@ -2793,7 +2869,7 @@ public class FHIRPathEngine { } else if (s.startsWith("%`ext-")) { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } else if (hostServices == null) { - throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); + throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); } else { return hostServices.resolveConstantType(context.appInfo, s); } @@ -2844,9 +2920,9 @@ public class FHIRPathEngine { } - private TypeDetails executeContextType(ExecutionTypeContext context, String name) throws PathEngineException, DefinitionException { + private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr) throws PathEngineException, DefinitionException { if (hostServices == null) { - throw makeException(I18nConstants.FHIRPATH_HO_HOST_SERVICES, "Context Reference"); + throw makeException(expr, I18nConstants.FHIRPATH_HO_HOST_SERVICES, "Context Reference"); } return hostServices.resolveConstantType(context.appInfo, name); } @@ -2856,7 +2932,7 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, type); } TypeDetails result = new TypeDetails(null); - getChildTypesByName(type, exp.getName(), result); + getChildTypesByName(type, exp.getName(), result, exp); return result; } @@ -2886,15 +2962,15 @@ public class FHIRPathEngine { case Not : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Exists : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case SubsetOf : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, focus); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case SupersetOf : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, focus); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case IsDistinct : @@ -2914,16 +2990,16 @@ public class FHIRPathEngine { case Aggregate : return anything(focus.getCollectionStatus()); case Item : { - checkOrdered(focus, "item"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "item", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case As : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); } case OfType : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); } case Type : { @@ -2942,31 +3018,31 @@ public class FHIRPathEngine { } } case Is : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Single : return focus.toSingleton(); case First : { - checkOrdered(focus, "first"); + checkOrdered(focus, "first", exp); return focus.toSingleton(); } case Last : { - checkOrdered(focus, "last"); + checkOrdered(focus, "last", exp); return focus.toSingleton(); } case Tail : { - checkOrdered(focus, "tail"); + checkOrdered(focus, "tail", exp); return focus; } case Skip : { - checkOrdered(focus, "skip"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "skip", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case Take : { - checkOrdered(focus, "take"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkOrdered(focus, "take", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return focus; } case Union : { @@ -2990,76 +3066,76 @@ public class FHIRPathEngine { return types; } case Lower : { - checkContextString(focus, "lower"); + checkContextString(focus, "lower", exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Upper : { - checkContextString(focus, "upper"); + checkContextString(focus, "upper", exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case ToChars : { - checkContextString(focus, "toChars"); + checkContextString(focus, "toChars", exp); return new TypeDetails(CollectionStatus.ORDERED, TypeDetails.FP_String); } case IndexOf : { - checkContextString(focus, "indexOf"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "indexOf", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Substring : { - checkContextString(focus, "subString"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkContextString(focus, "subString", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case StartsWith : { - checkContextString(focus, "startsWith"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "startsWith", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case EndsWith : { - checkContextString(focus, "endsWith"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "endsWith", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Matches : { - checkContextString(focus, "matches"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "matches", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ReplaceMatches : { - checkContextString(focus, "replaceMatches"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "replaceMatches", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Contains : { - checkContextString(focus, "contains"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "contains", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Replace : { - checkContextString(focus, "replace"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, "string"), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "replace", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, "string"), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Length : { - checkContextPrimitive(focus, "length", false); + checkContextPrimitive(focus, "length", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Children : - return childTypes(focus, "*"); + return childTypes(focus, "*", exp); case Descendants : - return childTypes(focus, "**"); + return childTypes(focus, "**", exp); case MemberOf : { - checkContextCoded(focus, "memberOf"); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextCoded(focus, "memberOf", exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Trace : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; } case Check : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; } case Today : @@ -3067,11 +3143,11 @@ public class FHIRPathEngine { case Now : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); case Resolve : { - checkContextReference(focus, "resolve"); + checkContextReference(focus, "resolve", exp); return new TypeDetails(CollectionStatus.SINGLETON, "DomainResource"); } case Extension : { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, "Extension"); } case AnyTrue: @@ -3089,62 +3165,62 @@ public class FHIRPathEngine { case HtmlChecks2 : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); case Alias : - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return anything(CollectionStatus.SINGLETON); case AliasAs : - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return focus; case Encode: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Decode: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Escape: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Unescape: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Trim: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Split: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case Join: - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); case ToInteger : { - checkContextPrimitive(focus, "toInteger", true); + checkContextPrimitive(focus, "toInteger", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case ToDecimal : { - checkContextPrimitive(focus, "toDecimal", true); + checkContextPrimitive(focus, "toDecimal", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } case ToString : { - checkContextPrimitive(focus, "toString", true); + checkContextPrimitive(focus, "toString", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case ToQuantity : { - checkContextPrimitive(focus, "toQuantity", true); + checkContextPrimitive(focus, "toQuantity", true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); } case ToBoolean : { - checkContextPrimitive(focus, "toBoolean", false); + checkContextPrimitive(focus, "toBoolean", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ToDateTime : { - checkContextPrimitive(focus, "ToDateTime", false); + checkContextPrimitive(focus, "ToDateTime", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); } case ToTime : { - checkContextPrimitive(focus, "ToTime", false); + checkContextPrimitive(focus, "ToTime", false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); } case ConvertsToString : case ConvertsToQuantity :{ - checkContextPrimitive(focus, exp.getFunction().toCode(), true); + checkContextPrimitive(focus, exp.getFunction().toCode(), true, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ConvertsToInteger : @@ -3153,28 +3229,28 @@ public class FHIRPathEngine { case ConvertsToDate : case ConvertsToTime : case ConvertsToBoolean : { - checkContextPrimitive(focus, exp.getFunction().toCode(), false); + checkContextPrimitive(focus, exp.getFunction().toCode(), false, exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ConformsTo: { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Abs : { - checkContextNumerical(focus, "abs"); + checkContextNumerical(focus, "abs", exp); return new TypeDetails(CollectionStatus.SINGLETON, focus.getTypes()); } case Truncate : case Floor : case Ceiling : { - checkContextDecimal(focus, exp.getFunction().toCode()); + checkContextDecimal(focus, exp.getFunction().toCode(), exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Round :{ - checkContextDecimal(focus, "round"); + checkContextDecimal(focus, "round", exp); if (paramTypes.size() > 0) { - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); } return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } @@ -3182,17 +3258,17 @@ public class FHIRPathEngine { case Exp : case Ln : case Sqrt : { - checkContextNumerical(focus, exp.getFunction().toCode()); + checkContextNumerical(focus, exp.getFunction().toCode(), exp); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } case Log : { - checkContextNumerical(focus, exp.getFunction().toCode()); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); + checkContextNumerical(focus, exp.getFunction().toCode(), exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); } case Power : { - checkContextNumerical(focus, exp.getFunction().toCode()); - checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); + checkContextNumerical(focus, exp.getFunction().toCode(), exp); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); return new TypeDetails(CollectionStatus.SINGLETON, focus.getTypes()); } @@ -3206,7 +3282,7 @@ public class FHIRPathEngine { } - private void checkParamTypes(String funcName, List paramTypes, TypeDetails... typeSet) throws PathEngineException { + private void checkParamTypes(ExpressionNode expr, String funcName, List paramTypes, TypeDetails... typeSet) throws PathEngineException { int i = 0; for (TypeDetails pt : typeSet) { if (i == paramTypes.size()) { @@ -3216,65 +3292,65 @@ public class FHIRPathEngine { i++; for (String a : actual.getTypes()) { if (!pt.hasType(worker, a)) { - throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, funcName, i, a, pt.toString()); + throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, funcName, i, a, pt.toString()); } } } } - private void checkOrdered(TypeDetails focus, String name) throws PathEngineException { + private void checkOrdered(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (focus.getCollectionStatus() == CollectionStatus.UNORDERED) { - throw makeException(I18nConstants.FHIRPATH_ORDERED_ONLY, name); + throw makeException(expr, I18nConstants.FHIRPATH_ORDERED_ONLY, name); } } - private void checkContextReference(TypeDetails focus, String name) throws PathEngineException { + private void checkContextReference(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "Reference") && !focus.hasType(worker, "canonical")) { - throw makeException(I18nConstants.FHIRPATH_REFERENCE_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_REFERENCE_ONLY, name, focus.describe()); } } - private void checkContextCoded(TypeDetails focus, String name) throws PathEngineException { + private void checkContextCoded(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "Coding") && !focus.hasType(worker, "CodeableConcept")) { - throw makeException(I18nConstants.FHIRPATH_CODED_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_CODED_ONLY, name, focus.describe()); } } - private void checkContextString(TypeDetails focus, String name) throws PathEngineException { + private void checkContextString(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "canonical") && !focus.hasType(worker, "id")) { - throw makeException(I18nConstants.FHIRPATH_STRING_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_STRING_ONLY, name, focus.describe()); } } - private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty) throws PathEngineException { + private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty, ExpressionNode expr) throws PathEngineException { if (canQty) { if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) { - throw makeException(I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString()); + throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString()); } } else if (!focus.hasType(primitiveTypes)) { - throw makeException(I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), primitiveTypes.toString()); + throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), primitiveTypes.toString()); } } - private void checkContextNumerical(TypeDetails focus, String name) throws PathEngineException { + private void checkContextNumerical(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType("integer") && !focus.hasType("decimal") && !focus.hasType("Quantity")) { - throw makeException(I18nConstants.FHIRPATH_NUMERICAL_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_NUMERICAL_ONLY, name, focus.describe()); } } - private void checkContextDecimal(TypeDetails focus, String name) throws PathEngineException { + private void checkContextDecimal(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { if (!focus.hasType("decimal") && !focus.hasType("integer")) { - throw makeException(I18nConstants.FHIRPATH_DECIMAL_ONLY, name, focus.describe()); + throw makeException(expr, I18nConstants.FHIRPATH_DECIMAL_ONLY, name, focus.describe()); } } - private TypeDetails childTypes(TypeDetails focus, String mask) throws PathEngineException, DefinitionException { + private TypeDetails childTypes(TypeDetails focus, String mask, ExpressionNode expr) throws PathEngineException, DefinitionException { TypeDetails result = new TypeDetails(CollectionStatus.UNORDERED); for (String f : focus.getTypes()) { - getChildTypesByName(f, mask, result); + getChildTypesByName(f, mask, result, expr); } return result; } @@ -3394,9 +3470,9 @@ public class FHIRPathEngine { } } - private List funcSqrt(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcSqrt(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "sqrt", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "sqrt", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3408,15 +3484,15 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcAbs(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcAbs(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "abs", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "abs", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3431,15 +3507,15 @@ public class FHIRPathEngine { Quantity qty = (Quantity) base; result.add(qty.copy().setValue(qty.getValue().abs())); } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "abs", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "abs", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcCeiling(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcCeiling(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "ceiling", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "ceiling", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3450,14 +3526,14 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ceiling", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ceiling", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcFloor(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcFloor(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "floor", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "floor", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3469,15 +3545,15 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "floor", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "floor", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcExp(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcExp(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "exp", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "exp", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3490,15 +3566,15 @@ public class FHIRPathEngine { } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "exp", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "exp", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcLn(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcLn(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "ln", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "ln", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3510,22 +3586,22 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ln", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ln", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcLog(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcLog(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "log", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "log", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { - List n1 = execute(context, focus, exp.getParameters().get(0), true); + List n1 = execute(context, focus, expr.getParameters().get(0), true); if (n1.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "0", "Multiple Values", "integer or decimal"); + throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "0", "Multiple Values", "integer or decimal"); } Double e = Double.parseDouble(n1.get(0).primitiveValue()); Double d = Double.parseDouble(base.primitiveValue()); @@ -3535,7 +3611,7 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "(focus)", base.fhirType(), "integer or decimal"); } return result; } @@ -3544,16 +3620,16 @@ public class FHIRPathEngine { return Math.log(logNumber) / Math.log(base); } - private List funcPower(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcPower(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "power", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "power", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { - List n1 = execute(context, focus, exp.getParameters().get(0), true); + List n1 = execute(context, focus, expr.getParameters().get(0), true); if (n1.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer or decimal"); + throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer or decimal"); } Double e = Double.parseDouble(n1.get(0).primitiveValue()); Double d = Double.parseDouble(base.primitiveValue()); @@ -3563,14 +3639,14 @@ public class FHIRPathEngine { // just return nothing } } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcTruncate(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcTruncate(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "truncate", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "truncate", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); @@ -3581,30 +3657,30 @@ public class FHIRPathEngine { } result.add(new IntegerType(s)); } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); } return result; } - private List funcRound(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcRound(ExecutionContext context, List focus, ExpressionNode expr) { if (focus.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "round", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "round", focus.size()); } Base base = focus.get(0); List result = new ArrayList(); if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { int i = 0; - if (exp.getParameters().size() == 1) { - List n1 = execute(context, focus, exp.getParameters().get(0), true); + if (expr.getParameters().size() == 1) { + List n1 = execute(context, focus, expr.getParameters().get(0), true); if (n1.size() != 1) { - throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer"); + throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer"); } i = Integer.parseInt(n1.get(0).primitiveValue()); } BigDecimal d = new BigDecimal (base.primitiveValue()); result.add(new DecimalType(d.setScale(i, RoundingMode.HALF_UP))); } else { - makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "round", "(focus)", base.fhirType(), "integer or decimal"); + makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "round", "(focus)", base.fhirType(), "integer or decimal"); } return result; } @@ -3843,7 +3919,7 @@ public class FHIRPathEngine { for (Base item : focus) { pc.clear(); pc.add(item); - Equality eq = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true)); + Equality eq = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); if (eq != Equality.True) { all = false; break; @@ -3942,7 +4018,7 @@ public class FHIRPathEngine { } - private List funcReplace(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException, PathEngineException { + private List funcReplace(ExecutionContext context, List focus, ExpressionNode expr) throws FHIRException, PathEngineException { List result = new ArrayList(); if (focus.size() == 1) { @@ -3950,13 +4026,13 @@ public class FHIRPathEngine { if (Utilities.noString(f)) { result.add(new StringType("")); } else { - String t = convertToString(execute(context, focus, exp.getParameters().get(0), true)); - String r = convertToString(execute(context, focus, exp.getParameters().get(1), true)); + String t = convertToString(execute(context, focus, expr.getParameters().get(0), true)); + String r = convertToString(execute(context, focus, expr.getParameters().get(1), true)); String n = f.replace(t, r); result.add(new StringType(n)); } } else { - throw makeException(I18nConstants.FHIRPATH_NO_COLLECTION, "replace", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_NO_COLLECTION, "replace", focus.size()); } return result; } @@ -4049,22 +4125,22 @@ public class FHIRPathEngine { return result; } - private List funcToDateTime(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcToDateTime(ExecutionContext context, List focus, ExpressionNode expr) { // List result = new ArrayList(); // result.add(new BooleanType(convertToBoolean(focus))); // return result; - throw makeException(I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toDateTime"); + throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toDateTime"); } - private List funcToTime(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcToTime(ExecutionContext context, List focus, ExpressionNode expr) { // List result = new ArrayList(); // result.add(new BooleanType(convertToBoolean(focus))); // return result; - throw makeException(I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toTime"); + throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toTime"); } - private List funcToDecimal(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcToDecimal(ExecutionContext context, List focus, ExpressionNode expr) { String s = convertToString(focus); List result = new ArrayList(); if (Utilities.isDecimal(s, true)) { @@ -4082,7 +4158,7 @@ public class FHIRPathEngine { private List funcIif(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List n1 = execute(context, focus, exp.getParameters().get(0), true); - Equality v = asBool(n1); + Equality v = asBool(n1, exp); if (v == Equality.True) { return execute(context, focus, exp.getParameters().get(1), true); @@ -4157,28 +4233,28 @@ public class FHIRPathEngine { } - private List funcSingle(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { + private List funcSingle(ExecutionContext context, List focus, ExpressionNode expr) throws PathEngineException { if (focus.size() == 1) { return focus; } - throw makeException(I18nConstants.FHIRPATH_NO_COLLECTION, "single", focus.size()); + throw makeException(expr, I18nConstants.FHIRPATH_NO_COLLECTION, "single", focus.size()); } - private List funcIs(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { + private List funcIs(ExecutionContext context, List focus, ExpressionNode expr) throws PathEngineException { if (focus.size() == 0 || focus.size() > 1) { return makeNull(); } String ns = null; String n = null; - ExpressionNode texp = exp.getParameters().get(0); + ExpressionNode texp = expr.getParameters().get(0); if (texp.getKind() != Kind.Name) { - throw makeException(I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "0", "is"); + throw makeException(expr, I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "0", "is"); } if (texp.getInner() != null) { if (texp.getInner().getKind() != Kind.Name) { - throw makeException(I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "1", "is"); + throw makeException(expr, I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "1", "is"); } ns = texp.getName(); n = texp.getInner().getName(); @@ -4214,13 +4290,13 @@ public class FHIRPathEngine { } - private List funcAs(ExecutionContext context, List focus, ExpressionNode exp) { + private List funcAs(ExecutionContext context, List focus, ExpressionNode expr) { List result = new ArrayList(); String tn; - if (exp.getParameters().get(0).getInner() != null) { - tn = exp.getParameters().get(0).getName()+"."+exp.getParameters().get(0).getInner().getName(); + if (expr.getParameters().get(0).getInner() != null) { + tn = expr.getParameters().get(0).getName()+"."+expr.getParameters().get(0).getInner().getName(); } else { - tn = "FHIR."+exp.getParameters().get(0).getName(); + tn = "FHIR."+expr.getParameters().get(0).getName(); } for (Base b : focus) { if (tn.startsWith("System.")) { @@ -4366,7 +4442,7 @@ public class FHIRPathEngine { if (exp.getParameters().size() == 1) { pc.clear(); pc.add(f); - Equality v = asBool(execute(changeThis(context, f), pc, exp.getParameters().get(0), true)); + Equality v = asBool(execute(changeThis(context, f), pc, exp.getParameters().get(0), true), exp); if (v == Equality.True) { empty = false; } @@ -4461,7 +4537,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v != Equality.False) { all = false; break; @@ -4495,7 +4571,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v == Equality.False) { any = true; break; @@ -4529,7 +4605,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v != Equality.True) { all = false; break; @@ -4562,7 +4638,7 @@ public class FHIRPathEngine { pc.clear(); pc.add(item); List res = execute(context, pc, exp.getParameters().get(0), true); - Equality v = asBool(res); + Equality v = asBool(res, exp); if (v == Equality.True) { any = true; break; @@ -4603,12 +4679,12 @@ public class FHIRPathEngine { return focus; } - private List funcCheck(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { - List n1 = execute(context, focus, exp.getParameters().get(0), true); + private List funcCheck(ExecutionContext context, List focus, ExpressionNode expr) throws FHIRException { + List n1 = execute(context, focus, expr.getParameters().get(0), true); if (!convertToBoolean(n1)) { - List n2 = execute(context, focus, exp.getParameters().get(1), true); + List n2 = execute(context, focus, expr.getParameters().get(1), true); String name = n2.get(0).primitiveValue(); - throw makeException(I18nConstants.FHIRPATH_CHECK_FAILED, name); + throw makeException(expr, I18nConstants.FHIRPATH_CHECK_FAILED, name); } return focus; } @@ -4870,15 +4946,15 @@ public class FHIRPathEngine { return result; } - private List funcConformsTo(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { + private List funcConformsTo(ExecutionContext context, List focus, ExpressionNode expr) throws FHIRException { if (hostServices == null) { - throw makeException(I18nConstants.FHIRPATH_HO_HOST_SERVICES, "conformsTo"); + throw makeException(expr, I18nConstants.FHIRPATH_HO_HOST_SERVICES, "conformsTo"); } List result = new ArrayList(); if (focus.size() != 1) { result.add(new BooleanType(false).noExtensions()); } else { - String url = convertToString(execute(context, focus, exp.getParameters().get(0), true)); + String url = convertToString(execute(context, focus, expr.getParameters().get(0), true)); result.add(new BooleanType(hostServices.conformsToProfile(context.appInfo, focus.get(0), url)).noExtensions()); } return result; @@ -5041,7 +5117,7 @@ public class FHIRPathEngine { for (Base item : focus) { pc.clear(); pc.add(item); - Equality v = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true)); + Equality v = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); if (v == Equality.True) { result.add(item); } @@ -5078,7 +5154,7 @@ public class FHIRPathEngine { private List funcNot(ExecutionContext context, List focus, ExpressionNode exp) throws PathEngineException { List result = new ArrayList(); - Equality v = asBool(focus); + Equality v = asBool(focus, exp); if (v != Equality.Null) { result.add(new BooleanType(v != Equality.True)); } @@ -5102,9 +5178,9 @@ public class FHIRPathEngine { } - private void getChildTypesByName(String type, String name, TypeDetails result) throws PathEngineException, DefinitionException { + private void getChildTypesByName(String type, String name, TypeDetails result, ExpressionNode expr) throws PathEngineException, DefinitionException { if (Utilities.noString(type)) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, "", "getChildTypesByName"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, "", "getChildTypesByName"); } if (type.equals("http://hl7.org/fhir/StructureDefinition/xhtml")) { return; @@ -5127,24 +5203,24 @@ public class FHIRPathEngine { String tail = ""; StructureDefinition sd = worker.fetchResource(StructureDefinition.class, url); if (sd == null) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, url, "getChildTypesByName"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, url, "getChildTypesByName"); } List sdl = new ArrayList(); ElementDefinitionMatch m = null; if (type.contains("#")) - m = getElementDefinition(sd, type.substring(type.indexOf("#")+1), false); + m = getElementDefinition(sd, type.substring(type.indexOf("#")+1), false, expr); if (m != null && hasDataType(m.definition)) { if (m.fixedType != null) { StructureDefinition dt = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(m.fixedType, worker.getOverrideVersionNs())); if (dt == null) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(m.fixedType, worker.getOverrideVersionNs()), "getChildTypesByName"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(m.fixedType, worker.getOverrideVersionNs()), "getChildTypesByName"); } sdl.add(dt); } else for (TypeRefComponent t : m.definition.getType()) { StructureDefinition dt = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(t.getCode(), worker.getOverrideVersionNs())); if (dt == null) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(t.getCode(), worker.getOverrideVersionNs()), "getChildTypesByName"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(t.getCode(), worker.getOverrideVersionNs()), "getChildTypesByName"); } sdl.add(dt); } @@ -5173,11 +5249,11 @@ public class FHIRPathEngine { if (t.getCode().equals("Resource")) { for (String rn : worker.getResourceNames()) { if (!result.hasType(worker, rn)) { - getChildTypesByName(result.addType(rn), "**", result); + getChildTypesByName(result.addType(rn), "**", result, expr); } } } else if (!result.hasType(worker, tn)) { - getChildTypesByName(result.addType(tn), "**", result); + getChildTypesByName(result.addType(tn), "**", result, expr); } } } @@ -5201,7 +5277,7 @@ public class FHIRPathEngine { } else { path = sdi.getSnapshot().getElement().get(0).getPath()+tail+"."+name; - ElementDefinitionMatch ed = getElementDefinition(sdi, path, isAllowPolymorphicNames()); + ElementDefinitionMatch ed = getElementDefinition(sdi, path, isAllowPolymorphicNames(), expr); if (ed != null) { if (!Utilities.noString(ed.getFixedType())) result.addType(ed.getFixedType()); @@ -5259,7 +5335,7 @@ public class FHIRPathEngine { } - private ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName) throws PathEngineException { + private ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName, ExpressionNode expr) throws PathEngineException { for (ElementDefinition ed : sd.getSnapshot().getElement()) { if (ed.getPath().equals(path)) { if (ed.hasContentReference()) { @@ -5286,13 +5362,13 @@ public class FHIRPathEngine { } StructureDefinition nsd = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(ed.getType().get(0).getCode(), worker.getOverrideVersionNs())); if (nsd == null) { - throw makeException(I18nConstants.FHIRPATH_NO_TYPE, ed.getType().get(0).getCode(), "getElementDefinition"); + throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ed.getType().get(0).getCode(), "getElementDefinition"); } - return getElementDefinition(nsd, nsd.getId()+path.substring(ed.getPath().length()), allowTypedName); + return getElementDefinition(nsd, nsd.getId()+path.substring(ed.getPath().length()), allowTypedName, expr); } if (ed.hasContentReference() && path.startsWith(ed.getPath()+".")) { ElementDefinitionMatch m = getElementDefinitionById(sd, ed.getContentReference()); - return getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName); + return getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName, expr); } } return null; @@ -5360,7 +5436,7 @@ public class FHIRPathEngine { if (element.hasSlicing()) { ElementDefinition slice = pickMandatorySlice(sd, element); if (slice == null) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_NAME_ALREADY_SLICED, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_NAME_ALREADY_SLICED, element.getId()); } element = slice; } @@ -5373,9 +5449,9 @@ public class FHIRPathEngine { // if that's empty, get the children of the type if (childDefinitions.isEmpty()) { - sd = fetchStructureByType(element); + sd = fetchStructureByType(element, expr); if (sd == null) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_THIS_CANNOT_FIND, element.getType().get(0).getProfile(), element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_THIS_CANNOT_FIND, element.getType().get(0).getProfile(), element.getId()); } childDefinitions = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); } @@ -5389,20 +5465,20 @@ public class FHIRPathEngine { } else if (expr.getKind() == Kind.Function) { if ("resolve".equals(expr.getName())) { if (!element.hasType()) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NO_TYPE, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NO_TYPE, element.getId()); } if (element.getType().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_MULTIPLE_TYPES, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_MULTIPLE_TYPES, element.getId()); } if (!element.getType().get(0).hasTarget()) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NOT_REFERENCE, element.getId(), element.getType().get(0).getCode()+")"); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NOT_REFERENCE, element.getId(), element.getType().get(0).getCode()+")"); } if (element.getType().get(0).getTargetProfile().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_NO_TARGET, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_NO_TARGET, element.getId()); } sd = worker.fetchResource(StructureDefinition.class, element.getType().get(0).getTargetProfile().get(0).getValue()); if (sd == null) { - throw makeException(I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_CANT_FIND, element.getType().get(0).getTargetProfile(), element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_CANT_FIND, element.getType().get(0).getTargetProfile(), element.getId()); } focus = sd.getSnapshot().getElementFirstRep(); } else if ("extension".equals(expr.getName())) { @@ -5426,13 +5502,13 @@ public class FHIRPathEngine { } } else if ("ofType".equals(expr.getName())) { if (!element.hasType()) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_TYPE_NONE, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_TYPE_NONE, element.getId()); } if (element.getType().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_TYPE_MULTIPLE, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_TYPE_MULTIPLE, element.getId()); } if (!element.getType().get(0).hasCode()) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_NO_CODE, element.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_NO_CODE, element.getId()); } String atn = element.getType().get(0).getCode(); String stn = expr.getParameters().get(0).getName(); @@ -5441,19 +5517,19 @@ public class FHIRPathEngine { focus = element; } } else { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_NAME, expr.getName()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_NAME, expr.getName()); } } else if (expr.getKind() == Kind.Group) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP); } else if (expr.getKind() == Kind.Constant) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST); } if (focus == null) { if (okToNotResolve) { return null; } else { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_CANT_FIND, expr.toString()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_CANT_FIND, expr.toString()); } } else if (expr.getInner() == null) { return focus; @@ -5473,15 +5549,15 @@ public class FHIRPathEngine { } - private StructureDefinition fetchStructureByType(ElementDefinition ed) throws DefinitionException { + private StructureDefinition fetchStructureByType(ElementDefinition ed, ExpressionNode expr) throws DefinitionException { if (ed.getType().size() == 0) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_NOTYPE, ed.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_NOTYPE, ed.getId()); } if (ed.getType().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_TYPES, ed.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_TYPES, ed.getId()); } if (ed.getType().get(0).getProfile().size() > 1) { - throw makeException(I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_PROFILES, ed.getId()); + throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_PROFILES, ed.getId()); } if (ed.getType().get(0).hasProfile()) { return worker.fetchResource(StructureDefinition.class, ed.getType().get(0).getProfile().get(0).getValue()); @@ -5509,7 +5585,7 @@ public class FHIRPathEngine { return path.substring(path.lastIndexOf(".") + 1); } - private Equality asBool(List items) throws PathEngineException { + private Equality asBool(List items, ExpressionNode expr) throws PathEngineException { if (items.size() == 0) { return Equality.Null; } else if (items.size() == 1 && items.get(0).isBooleanPrimitive()) { @@ -5517,7 +5593,7 @@ public class FHIRPathEngine { } else if (items.size() == 1) { return Equality.True; } else { - throw makeException(I18nConstants.FHIRPATH_UNABLE_BOOLEAN, convertToString(items)); + throw makeException(expr, I18nConstants.FHIRPATH_UNABLE_BOOLEAN, convertToString(items)); } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java index 0a46a7fcf..5a8d2fb93 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java @@ -62,9 +62,9 @@ import org.hl7.fhir.r5.model.ImplementationGuide.ImplementationGuideDependsOnCom import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackageIndexBuilder; -import org.hl7.fhir.utilities.cache.PackageGenerator.PackageType; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.NpmPackageIndexBuilder; +import org.hl7.fhir.utilities.npm.ToolsVersion; +import org.hl7.fhir.utilities.npm.PackageGenerator.PackageType; import com.google.gson.Gson; import com.google.gson.GsonBuilder; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java index 0573d8740..13408d869 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/CDARoundTripTests.java @@ -13,8 +13,8 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java index 108ea465a..22b3e3e21 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java @@ -176,6 +176,7 @@ public class FHIRPathTests { node = fp.parse(expression); Assertions.assertTrue(fail != TestResultType.SYNTAX, String.format("Expected exception didn't occur parsing %s", expression)); } catch (Exception e) { + System.out.println("Parsing Error: "+e.getMessage()); Assertions.assertTrue(fail == TestResultType.SYNTAX, String.format("Unexpected exception parsing %s: " + e.getMessage(), expression)); } @@ -193,6 +194,7 @@ public class FHIRPathTests { } Assertions.assertTrue(fail != TestResultType.SEMANTICS, String.format("Expected exception didn't occur checking %s", expression)); } catch (Exception e) { + System.out.println("Checking Error: "+e.getMessage()); Assertions.assertTrue(fail == TestResultType.SEMANTICS, String.format("Unexpected exception checking %s: " + e.getMessage(), expression)); node = null; } @@ -203,6 +205,7 @@ public class FHIRPathTests { outcome = fp.evaluate(res, node); Assertions.assertTrue(fail == TestResultType.OK, String.format("Expected exception didn't occur executing %s", expression)); } catch (Exception e) { + System.out.println("Execution Error: "+e.getMessage()); Assertions.assertTrue(fail == TestResultType.EXECUTION, String.format("Unexpected exception executing %s: " + e.getMessage(), expression)); node = null; } @@ -254,11 +257,11 @@ public class FHIRPathTests { Assertions.assertTrue(outcome.get(i).equalsDeep(q), String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString())); } else { Assertions.assertTrue(outcome.get(i) instanceof PrimitiveType, String.format("Outcome %d: Value should be a primitive type but was %s", i, outcome.get(i).fhirType())); - if (!(v.equals(((PrimitiveType) outcome.get(i)).asStringValue()))) { + if (!(v.equals(((PrimitiveType) outcome.get(i)).fpValue()))) { System.out.println(name); - System.out.println(String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression)); + System.out.println(String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, ((PrimitiveType) outcome.get(i)).fpValue(), expression)); } - Assertions.assertEquals(v, ((PrimitiveType) outcome.get(i)).asStringValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression)); + Assertions.assertEquals(v, ((PrimitiveType) outcome.get(i)).fpValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, ((PrimitiveType) outcome.get(i)).fpValue(), expression)); } } } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NpmPackageTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NpmPackageTests.java index c9233871f..b115e42ea 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NpmPackageTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NpmPackageTests.java @@ -11,7 +11,7 @@ import java.util.zip.ZipInputStream; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ParsingTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ParsingTests.java index 33bbb6ab8..99a5c4b3c 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ParsingTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ParsingTests.java @@ -43,9 +43,9 @@ import org.hl7.fhir.r5.utils.IResourceValidator; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.xml.XMLUtil; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java index b1cdb6b78..d18e7dff1 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java @@ -42,9 +42,9 @@ import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r5.utils.IResourceValidator; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.xml.XMLUtil; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/StructureMapUtilitiesTest.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/StructureMapUtilitiesTest.java index dc9285a84..f3aa9ea25 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/StructureMapUtilitiesTest.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/StructureMapUtilitiesTest.java @@ -12,8 +12,8 @@ import org.hl7.fhir.r5.model.StructureMap.StructureMapGroupRuleTargetComponent; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.StructureMapUtilities; import org.hl7.fhir.r5.utils.StructureMapUtilities.ITransformerServices; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/XmlParserTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/XmlParserTests.java index bda09cbf3..c7f61adda 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/XmlParserTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/XmlParserTests.java @@ -11,8 +11,8 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java index 1eb33bdeb..b26a5ce16 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java @@ -1,5 +1,8 @@ package org.hl7.fhir.exceptions; +import org.hl7.fhir.utilities.SourceLocation; +import org.hl7.fhir.utilities.Utilities; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -33,20 +36,62 @@ package org.hl7.fhir.exceptions; public class PathEngineException extends FHIRException { + private static final long serialVersionUID = 31969342112856390L; + private SourceLocation location; + private String expression; + public PathEngineException() { super(); } - public PathEngineException(String message, Throwable cause) { - super(message, cause); - } + public PathEngineException(String message, Throwable cause) { + super(message, cause); + } - public PathEngineException(String message) { - super(message); - } + public PathEngineException(String message) { + super(message); + } - public PathEngineException(Throwable cause) { + public PathEngineException(String message, SourceLocation location, String expression, Throwable cause) { + super(message+rep(location, expression), cause); + } + + public PathEngineException(String message, SourceLocation location, String expression) { + super(message+rep(location, expression)); + } + + private static String rep(SourceLocation loc, String expr) { + if (loc != null) { + if (loc.getLine() == 1) { + return " (@char "+loc.getColumn()+")"; + } else { + return " (@line "+loc.getLine()+" char "+loc.getColumn()+")"; + } + } else if (Utilities.noString(expr)) { // can happen in some contexts... + return " (@~"+expr+")"; + } else { + return ""; + } + } + + public PathEngineException(Throwable cause) { super(cause); } + public String getExpression() { + return expression; + } + + public void setExpression(String expression) { + this.expression = expression; + } + + public SourceLocation getLocation() { + return location; + } + + public void setLocation(SourceLocation location) { + this.location = location; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SourceLocation.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SourceLocation.java new file mode 100644 index 000000000..5ed864765 --- /dev/null +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SourceLocation.java @@ -0,0 +1,27 @@ +package org.hl7.fhir.utilities; + +public class SourceLocation { + private int line; + private int column; + public SourceLocation(int line, int column) { + super(); + this.line = line; + this.column = column; + } + public int getLine() { + return line; + } + public int getColumn() { + return column; + } + public void setLine(int line) { + this.line = line; + } + public void setColumn(int column) { + this.column = column; + } + + public String toString() { + return Integer.toString(line)+", "+Integer.toString(column); + } +} diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java index 83e69ed40..c74687d8b 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage; /* Copyright (c) 2011+, HL7, Inc. diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/BasePackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/BasePackageCacheManager.java similarity index 99% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/BasePackageCacheManager.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/BasePackageCacheManager.java index 5074396bd..c116eb058 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/BasePackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/BasePackageCacheManager.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; import org.apache.commons.lang3.Validate; import org.hl7.fhir.exceptions.FHIRException; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/CachingPackageClient.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CachingPackageClient.java similarity index 97% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/CachingPackageClient.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CachingPackageClient.java index 571dd59c9..e5812be8f 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/CachingPackageClient.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CachingPackageClient.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; import java.io.File; import java.io.FileInputStream; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java similarity index 99% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java index 4d6b9f11d..66f22cc17 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; /* Copyright (c) 2011+, HL7, Inc. @@ -40,8 +40,8 @@ import org.hl7.fhir.utilities.IniFile; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.NpmPackage.NpmPackageFolder; import org.hl7.fhir.utilities.json.JSONUtil; +import org.hl7.fhir.utilities.npm.NpmPackage.NpmPackageFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/IPackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/IPackageCacheManager.java similarity index 94% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/IPackageCacheManager.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/IPackageCacheManager.java index eaeeebc48..99e19e511 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/IPackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/IPackageCacheManager.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; import org.hl7.fhir.exceptions.FHIRException; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java similarity index 99% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java index f32ef1380..5f1415f62 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; /* Copyright (c) 2011+, HL7, Inc. @@ -61,10 +61,10 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackage.PackageResourceInformationSorter; -import org.hl7.fhir.utilities.cache.PackageGenerator.PackageType; import org.hl7.fhir.utilities.json.JSONUtil; import org.hl7.fhir.utilities.json.JsonTrackingParser; +import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformationSorter; +import org.hl7.fhir.utilities.npm.PackageGenerator.PackageType; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackageIndexBuilder.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackageIndexBuilder.java similarity index 97% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackageIndexBuilder.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackageIndexBuilder.java index 5ae42d265..8d14a449b 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackageIndexBuilder.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackageIndexBuilder.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; import java.io.File; import java.io.IOException; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java similarity index 99% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java index 16e92ae7b..18f9e434f 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageClient.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; import com.google.gson.JsonArray; import com.google.gson.JsonElement; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageGenerator.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageGenerator.java similarity index 99% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageGenerator.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageGenerator.java index a39e537a7..d4d73c1a0 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageGenerator.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageGenerator.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; /* Copyright (c) 2011+, HL7, Inc. diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageHacker.java similarity index 97% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageHacker.java index c9c2c7ebe..26e7d9637 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/PackageHacker.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageHacker.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; import java.io.File; import java.io.FileInputStream; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/ToolsVersion.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/ToolsVersion.java similarity index 97% rename from org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/ToolsVersion.java rename to org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/ToolsVersion.java index 7ece6c31e..018ad9c12 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/ToolsVersion.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/ToolsVersion.java @@ -1,4 +1,4 @@ -package org.hl7.fhir.utilities.cache; +package org.hl7.fhir.utilities.npm; /* Copyright (c) 2011+, HL7, Inc. diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 18bee95a6..f9d145fbf 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -133,11 +133,11 @@ Terminology_TX_Code_ValueSetMax = No code provided, and a code must be provided Terminology_TX_Code_ValueSet_Ext = No code provided, and a code should be provided from the value set {0} ({1}) Terminology_TX_Coding_Count = Expected {0} but found {1} coding elements Terminology_TX_Confirm_1 = Could not confirm that the codes provided are in the value set {0} and a code from this value set is required (class = {1}) -Terminology_TX_Confirm_2 = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (class = {1}) +Terminology_TX_Confirm_2 = Could not confirm that the codes provided are in the value set {0} and a code should come from this value set unless it has no suitable code (the validator cannot judge what is suitable) (class = {1}) Terminology_TX_Confirm_3 = Could not confirm that the codes provided are in the value set {0} and a code is recommended to come from this value set (class = {1}) Terminology_TX_Confirm_4a = The code provided ({2}) is not in the value set {0}, and a code from this value set is required: {1} Terminology_TX_Confirm_4b = The codes provided ({2}) are not in the value set {0}, and a code from this value set is required: {1} -Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code +Terminology_TX_Confirm_5 = Could not confirm that the codes provided are in the value set {0}, and a code should come from this value set unless it has no suitable code (the validator cannot judge what is suitable) Terminology_TX_Confirm_6 = Could not confirm that the codes provided are in the value set {0}, and a code is recommended to come from this value set Terminology_TX_Display_Wrong = Display should be ''{0}'' Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept @@ -148,16 +148,16 @@ Terminology_TX_NoValid_1 = None of the codes provided are in the value set {0} ( Terminology_TX_NoValid_10 = The code provided is not in the maximum value set {0} ({1}), and a code from this value set is required) (code = {2}#{3}) Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0} ({1}{2}) Terminology_TX_NoValid_12 = The Coding provided ({2}) is not in the value set {0}, and a code is required from this value set. {1} -Terminology_TX_NoValid_13 = The Coding provided ({2}) is not in the value set {0}, and a code should come from this value set unless it has no suitable code. {1} +Terminology_TX_NoValid_13 = The Coding provided ({2}) is not in the value set {0}, and a code should come from this value set unless it has no suitable code (the validator cannot judge what is suitable). {1} Terminology_TX_NoValid_14 = The Coding provided ({2}) is not in the value set {0}, and a code is recommended to come from this value set. {1} Terminology_TX_NoValid_15 = The value provided (''{0}'') could not be validated in the absence of a terminology server Terminology_TX_NoValid_16 = The value provided (''{0}'') is not in the value set {1} ({2}), and a code is required from this value set){3} -Terminology_TX_NoValid_17 = The value provided (''{0}'') is not in the value set {1} ({2}), and a code should come from this value set unless it has no suitable code){3} +Terminology_TX_NoValid_17 = The value provided (''{0}'') is not in the value set {1} ({2}), and a code should come from this value set unless it has no suitable code and the validator cannot judge what is suitable){3} Terminology_TX_NoValid_18 = The value provided (''{0}'') is not in the value set {1} ({2}), and a code is recommended to come from this value set){3} -Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}), and a code should come from this value set unless it has no suitable code) (codes = {2}) +Terminology_TX_NoValid_2 = None of the codes provided are in the value set {0} ({1}), and a code should come from this value set unless it has no suitable code and the validator cannot judge what is suitable) (codes = {2}) Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}), and a code is recommended to come from this value set) (codes = {2}) Terminology_TX_NoValid_4 = The Coding provided ({2}) is not in the value set {0}, and a code is required from this value set {1} -Terminology_TX_NoValid_5 = The Coding provided ({2}) is not in the value set {0}, and a code should come from this value set unless it has no suitable code {1} +Terminology_TX_NoValid_5 = The Coding provided ({2}) is not in the value set {0}, and a code should come from this value set unless it has no suitable code (the validator cannot judge what is suitable) {1} Terminology_TX_NoValid_6 = The Coding provided ({2}) is not in the value set {0}, and a code is recommended to come from this value set {1} Terminology_TX_NoValid_7 = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}), and a code from this value set is required) (codes = {2}) diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/CachingPackageClientTests.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/CachingPackageClientTests.java index 2e897e910..a9d592118 100644 --- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/CachingPackageClientTests.java +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/CachingPackageClientTests.java @@ -1,7 +1,7 @@ package org.hl7.fhir.utilities.tests; -import org.hl7.fhir.utilities.cache.CachingPackageClient; -import org.hl7.fhir.utilities.cache.PackageClient.PackageInfo; +import org.hl7.fhir.utilities.npm.CachingPackageClient; +import org.hl7.fhir.utilities.npm.PackageClient.PackageInfo; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/PackageCacheTests.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/PackageCacheTests.java index 8e8289fbd..05d38d4d8 100644 --- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/PackageCacheTests.java +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/PackageCacheTests.java @@ -1,9 +1,9 @@ package org.hl7.fhir.utilities.tests; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index c36db51d3..8e7b585a7 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -529,6 +529,21 @@ public class BaseValidator { return thePass; } + /** + * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails + * + * @param thePass + * Set this parameter to false if the validation does not pass + * @return Returns thePass (in other words, returns true if the rule did not fail validation) + */ + protected boolean warningOrHint(List errors, IssueType type, String path, boolean thePass, boolean warning, String msg, Object... theMessageArguments) { + if (!thePass) { + String message = context.formatMessage(msg, theMessageArguments); + addValidationMessage(errors, type, -1, -1, path, message, warning ? IssueSeverity.WARNING : IssueSeverity.INFORMATION, null); + } + return thePass; + } + /** * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails * diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index d01ed9700..506c54680 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -34,7 +34,13 @@ import org.hl7.fhir.r5.utils.*; import org.hl7.fhir.r5.utils.IResourceValidator.*; import org.hl7.fhir.r5.utils.StructureMapUtilities.ITransformerServices; import org.hl7.fhir.utilities.i18n.I18nConstants; +import org.hl7.fhir.utilities.npm.BasePackageCacheManager; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.PackageClient; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.hl7.fhir.validation.BaseValidator.ValidationControl; +import org.hl7.fhir.validation.Validator.QuestionnaireMode; import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageInstaller; import org.hl7.fhir.validation.instance.InstanceValidator; import org.hl7.fhir.utilities.IniFile; @@ -42,11 +48,6 @@ import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.PackageClient; -import org.hl7.fhir.utilities.cache.BasePackageCacheManager; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; import org.hl7.fhir.utilities.i18n.I18nBase; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -311,6 +312,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst private boolean showTimes; private List bundleValidationRules = new ArrayList<>(); private Map validationControl = new HashMap<>(); + private QuestionnaireMode questionnaireMode; private class AsteriskFilter implements FilenameFilter { String dir; @@ -531,7 +533,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst v = src.substring(src.indexOf("|")+1); src = src.substring(0, src.indexOf("|")); } - String pid = pcm.getPackageId(src); + String pid = explore ? pcm.getPackageId(src) : null; if (!Utilities.noString(pid)) return fetchByPackage(pid+(v == null ? "" : "#"+v)); else @@ -563,7 +565,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } else if ((src.matches(FilesystemPackageCacheManager.PACKAGE_REGEX) || src.matches(FilesystemPackageCacheManager.PACKAGE_VERSION_REGEX)) && !src.endsWith(".zip") && !src.endsWith(".tgz")) { return fetchByPackage(src); } - throw new FHIRException("Unable to find/resolve/read -ig "+src); + throw new FHIRException("Unable to find/resolve/read "+(explore ? "-ig " : "")+src); } private Map loadIgSourceForVersion(String src, boolean recursive, boolean explore, VersionSourceInformation versions) throws FHIRException, IOException { @@ -642,18 +644,26 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst // ok, having tried all that... now we'll just try to access it directly byte[] cnt; - if (stream == null) - cnt = fetchFromUrlSpecific(src, "application/json", true); - else + List errors = new ArrayList<>(); + if (stream != null) { cnt = TextFile.streamToBytes(stream); - + } else { + cnt = fetchFromUrlSpecific(src, "application/json", true, errors); + if (cnt == null) { + cnt = fetchFromUrlSpecific(src, "application/xml", true, errors); + } + } + if (cnt == null) { + throw new FHIRException("Unable to fetch content from "+src+" ("+errors.toString()+")"); + + } FhirFormat fmt = checkIsResource(cnt, src); if (fmt != null) { Map res = new HashMap(); res.put(Utilities.changeFileExt(src, "."+fmt.getExtension()), cnt); return res; } - throw new FHIRException("Unable to find/resolve/read -ig "+src); + throw new FHIRException("Unable to read content from "+src+": cannot determine format"); } private Map fetchVersionFromUrl(String src, boolean explore, VersionSourceInformation versions) throws FHIRException, IOException { @@ -687,7 +697,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst // ok, having tried all that... now we'll just try to access it directly byte[] cnt; if (stream == null) - cnt = fetchFromUrlSpecific(src, "application/json", true); + cnt = fetchFromUrlSpecific(src, "application/json", true, null); else cnt = TextFile.streamToBytes(stream); @@ -697,7 +707,20 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst res.put(Utilities.changeFileExt(src, "."+fmt.getExtension()), cnt); return res; } - throw new FHIRException("Unable to find/resolve/read -ig "+src); + String fn = Utilities.path("[tmp]", "fetch-resource-error-content.bin"); + TextFile.bytesToFile(cnt, fn); + System.out.println("Error Fetching "+src); + System.out.println("Some content was found, saved to "+fn); + System.out.println("1st 100 bytes = "+presentForDebugging(cnt)); + throw new FHIRException("Unable to find/resolve/read "+(explore ? "-ig " : "")+src); + } + + private String presentForDebugging(byte[] cnt) { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < Integer.min(cnt.length, 50); i++) { + b.append(Integer.toHexString(cnt[i])); + } + return b.toString(); } private InputStream fetchFromUrlSpecific(String source, boolean optional) throws FHIRException, IOException { @@ -713,13 +736,24 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } } - private byte[] fetchFromUrlSpecific(String source, String contentType, boolean optional) throws FHIRException, IOException { + private byte[] fetchFromUrlSpecific(String source, String contentType, boolean optional, List errors) throws FHIRException, IOException { try { - URL url = new URL(source+"?nocache=" + System.currentTimeMillis()); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setRequestProperty("Accept", contentType); - return TextFile.streamToBytes(conn.getInputStream()); + try { + // try with cache-busting option and then try withhout in case the server doesn't support that + URL url = new URL(source+"?nocache=" + System.currentTimeMillis()); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestProperty("Accept", contentType); + return TextFile.streamToBytes(conn.getInputStream()); + } catch (Exception e) { + URL url = new URL(source); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestProperty("Accept", contentType); + return TextFile.streamToBytes(conn.getInputStream()); + } } catch (IOException e) { + if (errors != null) { + errors.add("Error accessing "+source+": "+e.getMessage()); + } if (optional) return null; else @@ -1123,7 +1157,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } } - public void setQuestionnaires(List questionnaires) { + public void setQuestionnaireMode(Validator.QuestionnaireMode questionnaireMode) { + this.questionnaireMode = questionnaireMode; } public void setNative(boolean doNative) { @@ -1135,8 +1170,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst FhirFormat cntType = null; } - public Content loadContent(String source, String opName) throws FHIRException, IOException { - Map s = loadIgSource(source, false, false); + public Content loadContent(String source, String opName, boolean asIg) throws FHIRException, IOException { + Map s = loadIgSource(source, false, asIg); Content res = new Content(); if (s.size() != 1) throw new FHIRException("Unable to find resource " + source + " to "+opName); @@ -1192,7 +1227,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst InstanceValidator validator = getValidator(); for (String ref : refs) { - Content cnt = loadContent(ref, "validate"); + Content cnt = loadContent(ref, "validate", false); List messages = new ArrayList(); Element e = null; try { @@ -1264,7 +1299,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst List refs = new ArrayList(); handleSources(sources, refs); for (String ref : refs) { - Content cnt = loadContent(ref, "validate"); + Content cnt = loadContent(ref, "validate", false); String s = TextFile.bytesToString(cnt.focus); if (s.contains("http://hl7.org/fhir/3.0")) { versions.see("3.0", "Profile in "+ref); @@ -1293,7 +1328,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst TimeTracker.Session tts = context.clock().start("validation"); context.clock().milestone(); System.out.print(" Validate " + ref); - Content cnt = loadContent(ref, "validate"); + Content cnt = loadContent(ref, "validate", false); try { OperationOutcome outcome = validate(ref, cnt.focus, cnt.cntType, profiles); ToolingExtensions.addStringExtension(outcome, ToolingExtensions.EXT_OO_FILE, ref); @@ -1485,7 +1520,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } public org.hl7.fhir.r5.elementmodel.Element transform(String source, String map) throws FHIRException, IOException { - Content cnt = loadContent(source, "validate"); + Content cnt = loadContent(source, "validate", false); return transform(cnt.focus, cnt.cntType, map); } @@ -1530,7 +1565,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } public DomainResource generate(String source, String version) throws FHIRException, IOException, EOperationOutcome { - Content cnt = loadContent(source, "validate"); + Content cnt = loadContent(source, "validate", false); Resource res = loadResourceByVersion(version, cnt.focus, source); RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE); RendererFactory.factory(res, rc).render((DomainResource) res); @@ -1538,20 +1573,20 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } public void convert(String source, String output) throws FHIRException, IOException { - Content cnt = loadContent(source, "validate"); + Content cnt = loadContent(source, "validate", false); Element e = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType); Manager.compose(context, e, new FileOutputStream(output), (output.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML), OutputStyle.PRETTY, null); } public String evaluateFhirPath(String source, String expression) throws FHIRException, IOException { - Content cnt = loadContent(source, "validate"); + Content cnt = loadContent(source, "validate", false); FHIRPathEngine fpe = new FHIRPathEngine(context); Element e = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType); return fpe.evaluateToString(e, expression); } public StructureDefinition snapshot(String source, String version) throws FHIRException, IOException { - Content cnt = loadContent(source, "validate"); + Content cnt = loadContent(source, "validate", false); Resource res = loadResourceByVersion(version, cnt.focus, Utilities.getFileNameForName(source)); if (!(res instanceof StructureDefinition)) @@ -1595,6 +1630,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst validator.getImplementationGuides().addAll(igs); validator.getBundleValidationRules().addAll(bundleValidationRules); validator.getValidationControl().putAll(validationControl ); + validator.setQuestionnaireMode(questionnaireMode); return validator; } @@ -2054,7 +2090,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } public byte[] transformVersion(String source, String targetVer, FhirFormat format, Boolean canDoNative) throws FHIRException, IOException, Exception { - Content cnt = loadContent(source, "validate"); + Content cnt = loadContent(source, "validate", false); org.hl7.fhir.r5.elementmodel.Element src = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType); // if the src has a url, we try to use the java code diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java index 8b6a38bf3..be1da42f0 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java @@ -91,6 +91,8 @@ public class Validator { public enum EngineMode { VALIDATION, TRANSFORM, NARRATIVE, SNAPSHOT, SCAN, CONVERT, FHIRPATH, VERSION } + + public enum QuestionnaireMode { NONE, CHECK, REQUIRED } private static CliContext cliContext; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java index 1ed9893a9..eea13ac6c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java @@ -54,8 +54,9 @@ public class CliContext { @JsonProperty("igs") private List igs = new ArrayList(); - @JsonProperty("questionnaires") - private List questionnaires = new ArrayList(); + @JsonProperty("questionnaire") + private Validator.QuestionnaireMode questionnaireMode = Validator.QuestionnaireMode.CHECK; + @JsonProperty("profiles") private List profiles = new ArrayList(); @JsonProperty("sources") @@ -118,22 +119,14 @@ public class CliContext { return this; } - @JsonProperty("questionnaires") - public List getQuestionnaires() { - return questionnaires; + @JsonProperty("questionnaire") + public Validator.QuestionnaireMode getQuestionnaireMode() { + return questionnaireMode; } - @JsonProperty("questionnaires") - public CliContext setQuestionnaires(List questionnaires) { - this.questionnaires = questionnaires; - return this; - } - - public CliContext addQuestionnaire(String questionnaire) { - if (this.questionnaires == null) { - this.questionnaires = new ArrayList<>(); - } - this.questionnaires.add(questionnaire); + @JsonProperty("questionnaire") + public CliContext setQuestionnaireMode(Validator.QuestionnaireMode questionnaireMode) { + this.questionnaireMode = questionnaireMode; return this; } @@ -482,7 +475,7 @@ public class CliContext { Objects.equals(snomedCT, that.snomedCT) && Objects.equals(targetVer, that.targetVer) && Objects.equals(igs, that.igs) && - Objects.equals(questionnaires, that.questionnaires) && + Objects.equals(questionnaireMode, that.questionnaireMode) && Objects.equals(profiles, that.profiles) && Objects.equals(sources, that.sources) && Objects.equals(crumbTrails, that.crumbTrails) && @@ -494,6 +487,6 @@ public class CliContext { @Override public int hashCode() { - return Objects.hash(doNative, anyExtensionsAllowed, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, map, output, txServer, sv, txLog, mapLog, lang, fhirpath, snomedCT, targetVer, igs, questionnaires, profiles, sources, mode, locale, locations, crumbTrails, showTimes); + return Objects.hash(doNative, anyExtensionsAllowed, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, map, output, txServer, sv, txLog, mapLog, lang, fhirpath, snomedCT, targetVer, igs, questionnaireMode, profiles, sources, mode, locale, locations, crumbTrails, showTimes); } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java index 9626987e7..0dd0b3412 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java @@ -14,9 +14,9 @@ import org.hl7.fhir.r5.utils.IResourceValidator.ReferenceValidationPolicy; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities.VersionURLInfo; -import org.hl7.fhir.utilities.cache.BasePackageCacheManager; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.NpmPackage; +import org.hl7.fhir.utilities.npm.BasePackageCacheManager; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageInstaller; public class StandAloneValidatorFetcher implements IValidatorResourceFetcher { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java index 5ec8f9b56..0b8580e40 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java @@ -203,7 +203,7 @@ public class ValidationService { validator.loadIg(src, cliContext.isRecursive()); } System.out.print(" Get set... "); - validator.setQuestionnaires(cliContext.getQuestionnaires()); + validator.setQuestionnaireMode(cliContext.getQuestionnaireMode()); validator.setNative(cliContext.isDoNative()); validator.setHintAboutNonMustSupport(cliContext.isHintAboutNonMustSupport()); validator.setAnyExtensionsAllowed(cliContext.isAnyExtensionsAllowed()); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java index a57040513..09397e776 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java @@ -2,8 +2,8 @@ package org.hl7.fhir.validation.cli.utils; import org.hl7.fhir.r5.model.Constants; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.ToolsVersion; import java.io.IOException; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java index 68638cdfc..20211de73 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java @@ -122,8 +122,10 @@ public class Params { } else if (args[i].equals(QUESTIONNAIRE)) { if (i + 1 == args.length) throw new Error("Specified -questionnaire without indicating questionnaire file"); - else - cliContext.addQuestionnaire(args[++i]); + else { + String q = args[++i]; + cliContext.setQuestionnaireMode(Validator.QuestionnaireMode.valueOf(q)); + } } else if (args[i].equals(NATIVE)) { cliContext.setDoNative(true); } else if (args[i].equals(ASSUME_VALID_REST_REF)) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 0c7ab8330..4794d133a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -140,6 +140,7 @@ import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.validation.BaseValidator; +import org.hl7.fhir.validation.Validator.QuestionnaireMode; import org.hl7.fhir.validation.instance.type.BundleValidator; import org.hl7.fhir.validation.instance.type.CodeSystemValidator; import org.hl7.fhir.validation.instance.type.MeasureValidator; @@ -375,6 +376,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private boolean crumbTrails; private List bundleValidationRules = new ArrayList<>(); private boolean validateValueSetCodesOnTxServer = true; + private QuestionnaireMode questionnaireMode; public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) { super(theContext); @@ -3844,9 +3846,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (element.getType().equals("Observation")) { validateObservation(errors, element, stack); } else if (element.getType().equals("Questionnaire")) { - new QuestionnaireValidator(context, myEnableWhenEvaluator, fpe, timeTracker).validateQuestionannaire(errors, element, element, stack); + new QuestionnaireValidator(context, myEnableWhenEvaluator, fpe, timeTracker, questionnaireMode).validateQuestionannaire(errors, element, element, stack); } else if (element.getType().equals("QuestionnaireResponse")) { - new QuestionnaireValidator(context, myEnableWhenEvaluator, fpe, timeTracker).validateQuestionannaireResponse(hostContext, errors, element, stack); + new QuestionnaireValidator(context, myEnableWhenEvaluator, fpe, timeTracker, questionnaireMode).validateQuestionannaireResponse(hostContext, errors, element, stack); } else if (element.getType().equals("Measure")) { new MeasureValidator(context, timeTracker).validateMeasure(hostContext, errors, element, stack); } else if (element.getType().equals("MeasureReport")) { @@ -5081,4 +5083,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } + public void setQuestionnaireMode(QuestionnaireMode questionnaireMode) { + this.questionnaireMode = questionnaireMode; + } + + public QuestionnaireMode getQuestionnaireMode() { + return questionnaireMode; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java index 492a689af..d37ffa2a1 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java @@ -45,6 +45,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.validation.BaseValidator; import org.hl7.fhir.validation.TimeTracker; +import org.hl7.fhir.validation.Validator.QuestionnaireMode; import org.hl7.fhir.validation.instance.EnableWhenEvaluator; import org.hl7.fhir.validation.instance.EnableWhenEvaluator.QStack; import org.hl7.fhir.validation.instance.utils.NodeStack; @@ -56,13 +57,15 @@ public class QuestionnaireValidator extends BaseValidator { private EnableWhenEvaluator myEnableWhenEvaluator; private FHIRPathEngine fpe; + private QuestionnaireMode questionnaireMode; - public QuestionnaireValidator(IWorkerContext context, EnableWhenEvaluator myEnableWhenEvaluator, FHIRPathEngine fpe, TimeTracker timeTracker) { + public QuestionnaireValidator(IWorkerContext context, EnableWhenEvaluator myEnableWhenEvaluator, FHIRPathEngine fpe, TimeTracker timeTracker, QuestionnaireMode questionnaireMode) { super(context); source = Source.InstanceValidator; this.myEnableWhenEvaluator = myEnableWhenEvaluator; this.fpe = fpe; this.timeTracker = timeTracker; + this.questionnaireMode = questionnaireMode; } public void validateQuestionannaire(List errors, Element element, Element element2, NodeStack stack) { @@ -164,6 +167,9 @@ public class QuestionnaireValidator extends BaseValidator { } public void validateQuestionannaireResponse(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack) throws FHIRException { + if (questionnaireMode == QuestionnaireMode.NONE) { + return; + } Element q = element.getNamedChild("questionnaire"); String questionnaire = null; if (q != null) { @@ -179,9 +185,15 @@ public class QuestionnaireValidator extends BaseValidator { questionnaire = q.getChildValue("reference"); } } - if (hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, I18nConstants.QUESTIONNAIRE_QR_Q_NONE)) { + boolean ok = questionnaireMode == QuestionnaireMode.REQUIRED ? + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, I18nConstants.QUESTIONNAIRE_QR_Q_NONE) : + hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, I18nConstants.QUESTIONNAIRE_QR_Q_NONE); + if (ok) { Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); - if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire)) { + ok = questionnaireMode == QuestionnaireMode.REQUIRED ? + rule(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire) : + warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire); + if (ok) { boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java index a10173f81..b3d4c0e3c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java @@ -56,28 +56,28 @@ public class ValueSetValidator extends BaseValidator { List composes = vs.getChildrenByName("compose"); int cc = 0; for (Element compose : composes) { - validateValueSetCompose(errors, compose, stack.push(compose, cc, null, null), vs.getNamedChildValue("url")); + validateValueSetCompose(errors, compose, stack.push(compose, cc, null, null), vs.getNamedChildValue("url"), "retired".equals(vs.getNamedChildValue("url"))); cc++; } } } - private void validateValueSetCompose(List errors, Element compose, NodeStack stack, String vsid) { + private void validateValueSetCompose(List errors, Element compose, NodeStack stack, String vsid, boolean retired) { List includes = compose.getChildrenByName("include"); int ci = 0; for (Element include : includes) { - validateValueSetInclude(errors, include, stack.push(include, ci, null, null), vsid); + validateValueSetInclude(errors, include, stack.push(include, ci, null, null), vsid, retired); ci++; } List excludes = compose.getChildrenByName("exclude"); int ce = 0; for (Element exclude : excludes) { - validateValueSetInclude(errors, exclude, stack.push(exclude, ce, null, null), vsid); + validateValueSetInclude(errors, exclude, stack.push(exclude, ce, null, null), vsid, retired); ce++; } } - private void validateValueSetInclude(List errors, Element include, NodeStack stack, String vsid) { + private void validateValueSetInclude(List errors, Element include, NodeStack stack, String vsid, boolean retired) { String system = include.getChildValue("system"); String version = include.getChildValue("version"); List valuesets = include.getChildrenByName("valueSet"); @@ -125,9 +125,9 @@ public class ValueSetValidator extends BaseValidator { } for (VSCodingValidationRequest cv : batch) { if (version == null) { - warning(errors, IssueType.BUSINESSRULE, cv.getStack().getLiteralPath(), cv.getResult().isOk(), I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE, system, cv.getCoding().getCode()); + warningOrHint(errors, IssueType.BUSINESSRULE, cv.getStack().getLiteralPath(), cv.getResult().isOk(), !retired, I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE, system, cv.getCoding().getCode()); } else { - warning(errors, IssueType.BUSINESSRULE, cv.getStack().getLiteralPath(), cv.getResult().isOk(), I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER, system, version, cv.getCoding().getCode()); + warningOrHint(errors, IssueType.BUSINESSRULE, cv.getStack().getLiteralPath(), cv.getResult().isOk(), !retired, I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER, system, version, cv.getCoding().getCode()); } } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java index 099f203c8..6304f71d4 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java @@ -7,11 +7,11 @@ import java.util.List; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.CachingPackageClient; -import org.hl7.fhir.utilities.cache.PackageClient.PackageInfo; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.CachingPackageClient; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.ToolsVersion; +import org.hl7.fhir.utilities.npm.PackageClient.PackageInfo; public class PackageValidator { diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java index 367977e38..d0cdf5425 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java @@ -38,9 +38,9 @@ import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.NpmPackage; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.xhtml.XhtmlComposer; diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/R3R4ConversionTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/R3R4ConversionTests.java index 03a5ba941..ba1bcce23 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/R3R4ConversionTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/R3R4ConversionTests.java @@ -22,8 +22,8 @@ import org.hl7.fhir.r4.utils.StructureMapUtilities.ITransformerServices; import org.hl7.fhir.utilities.IniFile; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.junit.jupiter.api.Disabled; diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java index 0cb9c8016..1e65b8cb7 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java @@ -59,9 +59,8 @@ import org.hl7.fhir.utilities.CSFile; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.utilities.cache.FilesystemPackageCacheManager; -import org.hl7.fhir.utilities.cache.ToolsVersion; - +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.ToolsVersion; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; diff --git a/pom.xml b/pom.xml index 5829bbfe7..219d4e271 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 5.1.0 - 1.1.44 + 1.1.45-SNAPSHOT 5.6.2 3.0.0-M4 0.8.5