diff --git a/master-branch-pipeline.yml b/master-branch-pipeline.yml index 161b17f26..475fffa5b 100644 --- a/master-branch-pipeline.yml +++ b/master-branch-pipeline.yml @@ -11,6 +11,7 @@ pool: variables: - group: PGP_VAR_GROUP - group: SONATYPE_VAR_GROUP +- group: GIT_VAR_GROUP steps: # We need a valid signing key. @@ -43,24 +44,19 @@ steps: xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd"> + + github + $(GIT_USER_NAME) + $(GIT_PAT) + ossrh $(SONATYPE_USER) $(SONATYPE_PASS) - sonatype-nexus-snapshots - $(SONATYPE_USER) - $(SONATYPE_PASS) - - - sonatype-nexus-staging - $(SONATYPE_USER) - $(SONATYPE_PASS) - - - $(PGP_KEYNAME) - $(PGP_PASSPHRASE) + $(PGP_KEYNAME) + $(PGP_PASSPHRASE) @@ -81,9 +77,19 @@ steps: # Deploy the SNAPSHOT artifact to sonatype nexus. # This is done for the master branch merges only. - task: Maven@3 - displayName: 'Deploy $(module) to Sonatype staging' + displayName: 'Deploy to Sonatype staging' inputs: mavenPomFile: '$(System.DefaultWorkingDirectory)/pom.xml' goals: deploy - options: '--settings $(System.DefaultWorkingDirectory)/settings.xml -Pdeploy' + options: '--settings $(System.DefaultWorkingDirectory)/settings.xml -DdeployToSonatype' + publishJUnitResults: false + + # Deploy the SNAPSHOT artifact to GitHub packages. + # This is done for the master branch merges only. + - task: Maven@3 + displayName: 'Deploy to GitHub packages' + inputs: + mavenPomFile: '$(System.DefaultWorkingDirectory)/pom.xml' + goals: deploy + options: '--settings $(System.DefaultWorkingDirectory)/settings.xml -Dmaven.test.skip -DdeployToGitHub' publishJUnitResults: false diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index a2fe5b829..f013169f6 100644 --- a/org.hl7.fhir.convertors/pom.xml +++ b/org.hl7.fhir.convertors/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml 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/VersionConvertor_10_40.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_10_40.java index c9550402d..4db50132b 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_10_40.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_10_40.java @@ -2023,15 +2023,18 @@ public class VersionConvertor_10_40 { copyElement(src, tgt); if (src.hasBounds()) tgt.setBounds(convertType(src.getBounds())); - tgt.setCount(src.getCount()); + if (src.hasCount()) + tgt.setCount(src.getCount()); if (src.hasDurationElement()) tgt.setDurationElement(convertDecimal(src.getDurationElement())); if (src.hasDurationMaxElement()) tgt.setDurationMaxElement(convertDecimal(src.getDurationMaxElement())); if (src.hasDurationUnits()) tgt.setDurationUnitElement(convertUnitsOfTime(src.getDurationUnitsElement())); - tgt.setFrequency(src.getFrequency()); - tgt.setFrequencyMax(src.getFrequencyMax()); + if (src.hasFrequency()) + tgt.setFrequency(src.getFrequency()); + if (src.hasFrequencyMax()) + tgt.setFrequencyMax(src.getFrequencyMax()); if (src.hasPeriodElement()) tgt.setPeriodElement(convertDecimal(src.getPeriodElement())); if (src.hasPeriodMaxElement()) @@ -2051,15 +2054,18 @@ public class VersionConvertor_10_40 { copyElement(src, tgt); if (src.hasBounds()) tgt.setBounds(convertType(src.getBounds())); - tgt.setCount(src.getCount()); + if (src.hasCount()) + tgt.setCount(src.getCount()); if (src.hasDurationElement()) tgt.setDurationElement(convertDecimal(src.getDurationElement())); if (src.hasDurationMaxElement()) tgt.setDurationMaxElement(convertDecimal(src.getDurationMaxElement())); if (src.hasDurationUnit()) tgt.setDurationUnitsElement(convertUnitsOfTime(src.getDurationUnitElement())); - tgt.setFrequency(src.getFrequency()); - tgt.setFrequencyMax(src.getFrequencyMax()); + if (src.hasFrequency()) + tgt.setFrequency(src.getFrequency()); + if (src.hasFrequencyMax()) + tgt.setFrequencyMax(src.getFrequencyMax()); if (src.hasPeriodElement()) tgt.setPeriodElement(convertDecimal(src.getPeriodElement())); if (src.hasPeriodMaxElement()) @@ -3430,4 +3436,4 @@ public class VersionConvertor_10_40 { copyElement(src, tgt); return tgt; } -} \ No newline at end of file +} diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java index 4837eeb0f..af922aba6 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_30_40.java @@ -4,90 +4,7 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import org.hl7.fhir.convertors.conv30_40.Account30_40; -import org.hl7.fhir.convertors.conv30_40.ActivityDefinition30_40; -import org.hl7.fhir.convertors.conv30_40.AllergyIntolerance30_40; -import org.hl7.fhir.convertors.conv30_40.Appointment30_40; -import org.hl7.fhir.convertors.conv30_40.AppointmentResponse30_40; -import org.hl7.fhir.convertors.conv30_40.AuditEvent30_40; -import org.hl7.fhir.convertors.conv30_40.Basic30_40; -import org.hl7.fhir.convertors.conv30_40.Binary30_40; -import org.hl7.fhir.convertors.conv30_40.BodySite30_40; -import org.hl7.fhir.convertors.conv30_40.Bundle30_40; -import org.hl7.fhir.convertors.conv30_40.CapabilityStatement30_40; -import org.hl7.fhir.convertors.conv30_40.CarePlan30_40; -import org.hl7.fhir.convertors.conv30_40.CareTeam30_40; -import org.hl7.fhir.convertors.conv30_40.ClinicalImpression30_40; -import org.hl7.fhir.convertors.conv30_40.CodeSystem30_40; -import org.hl7.fhir.convertors.conv30_40.Communication30_40; -import org.hl7.fhir.convertors.conv30_40.CompartmentDefinition30_40; -import org.hl7.fhir.convertors.conv30_40.Composition30_40; -import org.hl7.fhir.convertors.conv30_40.ConceptMap30_40; -import org.hl7.fhir.convertors.conv30_40.Condition30_40; -import org.hl7.fhir.convertors.conv30_40.Consent30_40; -import org.hl7.fhir.convertors.conv30_40.Coverage30_40; -import org.hl7.fhir.convertors.conv30_40.DataElement30_40; -import org.hl7.fhir.convertors.conv30_40.DetectedIssue30_40; -import org.hl7.fhir.convertors.conv30_40.DeviceUseStatement30_40; -import org.hl7.fhir.convertors.conv30_40.DiagnosticReport30_40; -import org.hl7.fhir.convertors.conv30_40.DocumentReference30_40; -import org.hl7.fhir.convertors.conv30_40.Encounter30_40; -import org.hl7.fhir.convertors.conv30_40.Endpoint30_40; -import org.hl7.fhir.convertors.conv30_40.EpisodeOfCare30_40; -import org.hl7.fhir.convertors.conv30_40.ExpansionProfile30_40; -import org.hl7.fhir.convertors.conv30_40.FamilyMemberHistory30_40; -import org.hl7.fhir.convertors.conv30_40.Flag30_40; -import org.hl7.fhir.convertors.conv30_40.Goal30_40; -import org.hl7.fhir.convertors.conv30_40.GraphDefinition30_40; -import org.hl7.fhir.convertors.conv30_40.Group30_40; -import org.hl7.fhir.convertors.conv30_40.HealthcareService30_40; -import org.hl7.fhir.convertors.conv30_40.ImagingStudy30_40; -import org.hl7.fhir.convertors.conv30_40.Immunization30_40; -import org.hl7.fhir.convertors.conv30_40.ImplementationGuide30_40; -import org.hl7.fhir.convertors.conv30_40.Library30_40; -import org.hl7.fhir.convertors.conv30_40.Linkage30_40; -import org.hl7.fhir.convertors.conv30_40.List30_40; -import org.hl7.fhir.convertors.conv30_40.Location30_40; -import org.hl7.fhir.convertors.conv30_40.Media30_40; -import org.hl7.fhir.convertors.conv30_40.Medication30_40; -import org.hl7.fhir.convertors.conv30_40.MedicationAdministration30_40; -import org.hl7.fhir.convertors.conv30_40.MedicationDispense30_40; -import org.hl7.fhir.convertors.conv30_40.MedicationRequest30_40; -import org.hl7.fhir.convertors.conv30_40.MedicationStatement30_40; -import org.hl7.fhir.convertors.conv30_40.MessageDefinition30_40; -import org.hl7.fhir.convertors.conv30_40.MessageHeader30_40; -import org.hl7.fhir.convertors.conv30_40.NamingSystem30_40; -import org.hl7.fhir.convertors.conv30_40.Observation30_40; -import org.hl7.fhir.convertors.conv30_40.OperationDefinition30_40; -import org.hl7.fhir.convertors.conv30_40.OperationOutcome30_40; -import org.hl7.fhir.convertors.conv30_40.Organization30_40; -import org.hl7.fhir.convertors.conv30_40.Parameters30_40; -import org.hl7.fhir.convertors.conv30_40.Patient30_40; -import org.hl7.fhir.convertors.conv30_40.PaymentNotice30_40; -import org.hl7.fhir.convertors.conv30_40.Person30_40; -import org.hl7.fhir.convertors.conv30_40.PlanDefinition30_40; -import org.hl7.fhir.convertors.conv30_40.Practitioner30_40; -import org.hl7.fhir.convertors.conv30_40.PractitionerRole30_40; -import org.hl7.fhir.convertors.conv30_40.Procedure30_40; -import org.hl7.fhir.convertors.conv30_40.ProcedureRequest30_40; -import org.hl7.fhir.convertors.conv30_40.Provenance30_40; -import org.hl7.fhir.convertors.conv30_40.Questionnaire30_40; -import org.hl7.fhir.convertors.conv30_40.QuestionnaireResponse30_40; -import org.hl7.fhir.convertors.conv30_40.RelatedPerson30_40; -import org.hl7.fhir.convertors.conv30_40.RiskAssessment30_40; -import org.hl7.fhir.convertors.conv30_40.Schedule30_40; -import org.hl7.fhir.convertors.conv30_40.SearchParameter30_40; -import org.hl7.fhir.convertors.conv30_40.Sequence30_40; -import org.hl7.fhir.convertors.conv30_40.Slot30_40; -import org.hl7.fhir.convertors.conv30_40.Specimen30_40; -import org.hl7.fhir.convertors.conv30_40.StructureDefinition30_40; -import org.hl7.fhir.convertors.conv30_40.StructureMap30_40; -import org.hl7.fhir.convertors.conv30_40.Subscription30_40; -import org.hl7.fhir.convertors.conv30_40.Substance30_40; -import org.hl7.fhir.convertors.conv30_40.SupplyDelivery30_40; -import org.hl7.fhir.convertors.conv30_40.TestReport30_40; -import org.hl7.fhir.convertors.conv30_40.TestScript30_40; -import org.hl7.fhir.convertors.conv30_40.ValueSet30_40; +import org.hl7.fhir.convertors.conv30_40.*; import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.exceptions.FHIRException; @@ -5164,6 +5081,8 @@ public class VersionConvertor_30_40 { return Coverage30_40.convertCoverage((org.hl7.fhir.r4.model.Coverage) src); if (src instanceof org.hl7.fhir.r4.model.DetectedIssue) return DetectedIssue30_40.convertDetectedIssue((org.hl7.fhir.r4.model.DetectedIssue) src); + if (src instanceof org.hl7.fhir.r4.model.Device) + return Device30_40.convertDevice((org.hl7.fhir.r4.model.Device) src); if (src instanceof org.hl7.fhir.r4.model.DeviceUseStatement) return DeviceUseStatement30_40.convertDeviceUseStatement((org.hl7.fhir.r4.model.DeviceUseStatement) src); if (src instanceof org.hl7.fhir.r4.model.DiagnosticReport) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_40/Device30_40.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_40/Device30_40.java new file mode 100644 index 000000000..4e77bec9b --- /dev/null +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_40/Device30_40.java @@ -0,0 +1,258 @@ +package org.hl7.fhir.convertors.conv30_40; + + +import org.hl7.fhir.convertors.VersionConvertor_30_40; +import org.hl7.fhir.dstu3.model.Device; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r4.model.Patient; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ +// Generated on Sun, Feb 24, 2019 11:37+1100 for FHIR v4.0.0 +public class Device30_40 extends VersionConvertor_30_40 { + + public static org.hl7.fhir.r4.model.Device convertDevice(org.hl7.fhir.dstu3.model.Device src) throws FHIRException { + if (src == null) + return null; + org.hl7.fhir.r4.model.Device tgt = new org.hl7.fhir.r4.model.Device(); + copyDomainResource(src, tgt); + for (org.hl7.fhir.dstu3.model.Identifier t : src.getIdentifier()) tgt.addIdentifier(convertIdentifier(t)); + if (src.hasUdi()) { + org.hl7.fhir.r4.model.Device.DeviceUdiCarrierComponent carrierComponent = tgt.getUdiCarrierFirstRep(); + carrierComponent.setDeviceIdentifierElement(VersionConvertor_30_40.convertString(src.getUdi().getDeviceIdentifierElement())); + carrierComponent.setJurisdictionElement(VersionConvertor_30_40.convertUri(src.getUdi().getJurisdictionElement())); + carrierComponent.setCarrierHRFElement(VersionConvertor_30_40.convertString(src.getUdi().getCarrierHRFElement())); + carrierComponent.setCarrierAIDCElement(VersionConvertor_30_40.convertBase64Binary(src.getUdi().getCarrierAIDCElement())); + carrierComponent.setIssuerElement(VersionConvertor_30_40.convertUri(src.getUdi().getIssuerElement())); + carrierComponent.setEntryTypeElement(convertUDIEntryType(src.getUdi().getEntryTypeElement())); + tgt.setUdiCarrier(Collections.singletonList(carrierComponent)); + org.hl7.fhir.r4.model.Device.DeviceDeviceNameComponent nameComponent = tgt.getDeviceNameFirstRep(); + nameComponent.setNameElement(VersionConvertor_30_40.convertString(src.getUdi().getNameElement())); + nameComponent.setType(org.hl7.fhir.r4.model.Device.DeviceNameType.UDILABELNAME); + } + if (src.hasStatus()) + tgt.setStatusElement(convertFHIRDeviceStatus(src.getStatusElement())); + if (src.hasType()) + tgt.setType(convertCodeableConcept(src.getType())); + if (src.hasLotNumber()) + tgt.setLotNumberElement(convertString(src.getLotNumberElement())); + if (src.hasManufacturer()) + tgt.setManufacturerElement(convertString(src.getManufacturerElement())); + if (src.hasManufactureDate()) + tgt.setManufactureDateElement(convertDateTime(src.getManufactureDateElement())); + if (src.hasExpirationDate()) + tgt.setExpirationDateElement(convertDateTime(src.getExpirationDateElement())); + if (src.hasModelElement()) + tgt.setModelNumberElement(VersionConvertor_30_40.convertString(src.getModelElement())); + if (src.hasVersionElement()) + tgt.setVersion(Collections.singletonList(tgt.getVersionFirstRep().setValueElement(VersionConvertor_30_40.convertString(src.getVersionElement())))); + if (src.hasPatient()) + tgt.setPatient(convertReference(src.getPatient())); + if (src.hasOwner()) + tgt.setOwner(convertReference(src.getOwner())); + if (src.hasContact()) + tgt.setContact(src.getContact().stream().map(VersionConvertor_30_40::convertContactPoint).collect(Collectors.toList())); + if (src.hasLocation()) + tgt.setLocation(convertReference(src.getLocation())); + if (src.hasUrl()) + tgt.setUrlElement(convertUri(src.getUrlElement())); + for (org.hl7.fhir.dstu3.model.Annotation t : src.getNote()) tgt.addNote(convertAnnotation(t)); + for (org.hl7.fhir.dstu3.model.CodeableConcept t : src.getSafety()) tgt.addSafety(convertCodeableConcept(t)); + return tgt; + } + + public static org.hl7.fhir.dstu3.model.Device convertDevice(org.hl7.fhir.r4.model.Device src) throws FHIRException { + if (src == null) + return null; + org.hl7.fhir.dstu3.model.Device tgt = new org.hl7.fhir.dstu3.model.Device(); + copyDomainResource(src, tgt); + for (org.hl7.fhir.r4.model.Identifier t : src.getIdentifier()) tgt.addIdentifier(convertIdentifier(t)); + if (src.hasUdiCarrier()) { + Device.DeviceUdiComponent udi = tgt.getUdi(); + udi.setDeviceIdentifierElement(VersionConvertor_30_40.convertString(src.getUdiCarrierFirstRep().getDeviceIdentifierElement())); + udi.setJurisdictionElement(VersionConvertor_30_40.convertUri(src.getUdiCarrierFirstRep().getJurisdictionElement())); + udi.setCarrierHRFElement(VersionConvertor_30_40.convertString(src.getUdiCarrierFirstRep().getCarrierHRFElement())); + udi.setCarrierAIDCElement(VersionConvertor_30_40.convertBase64Binary(src.getUdiCarrierFirstRep().getCarrierAIDCElement())); + udi.setIssuerElement(VersionConvertor_30_40.convertUri(src.getUdiCarrierFirstRep().getIssuerElement())); + udi.setEntryTypeElement(convertUDIEntryType(src.getUdiCarrierFirstRep().getEntryTypeElement())); + tgt.setUdi(udi); + } + if (src.hasStatus()) + tgt.setStatusElement(convertFHIRDeviceStatus(src.getStatusElement())); + if (src.hasType()) + tgt.setType(convertCodeableConcept(src.getType())); + if (src.hasLotNumber()) + tgt.setLotNumberElement(convertString(src.getLotNumberElement())); + if (src.hasManufacturer()) + tgt.setManufacturerElement(convertString(src.getManufacturerElement())); + if (src.hasManufactureDate()) + tgt.setManufactureDateElement(convertDateTime(src.getManufactureDateElement())); + if (src.hasExpirationDate()) + tgt.setExpirationDateElement(convertDateTime(src.getExpirationDateElement())); + if (src.hasModelNumber()) + tgt.setModel(src.getModelNumber()); + if (src.hasVersion()) + tgt.setVersionElement(VersionConvertor_30_40.convertString(src.getVersion().get(0).getValueElement())); + if (src.hasDeviceName()) + tgt.setUdi(tgt.getUdi().setName(src.getDeviceName().get(0).getName())); + if (src.hasPatient()) + tgt.setPatient(convertReference(src.getPatient())); + if (src.hasOwner()) + tgt.setOwner(convertReference(src.getOwner())); + if (src.hasContact()) + tgt.setContact(src.getContact().stream().map(VersionConvertor_30_40::convertContactPoint).collect(Collectors.toList())); + if (src.hasLocation()) + tgt.setLocation(convertReference(src.getLocation())); + for (org.hl7.fhir.r4.model.ContactPoint t : src.getContact()) tgt.addContact(convertContactPoint(t)); + if (src.hasLocation()) + tgt.setLocation(VersionConvertor_30_40.convertReference(src.getLocation())); + if (src.hasUrl()) + tgt.setUrlElement(convertUri(src.getUrlElement())); + for (org.hl7.fhir.r4.model.Annotation t : src.getNote()) tgt.addNote(convertAnnotation(t)); + for (org.hl7.fhir.r4.model.CodeableConcept t : src.getSafety()) tgt.addSafety(convertCodeableConcept(t)); + return tgt; + } + + static public org.hl7.fhir.r4.model.Enumeration convertFHIRDeviceStatus(org.hl7.fhir.dstu3.model.Enumeration src) throws FHIRException { + if (src == null || src.isEmpty()) + return null; + org.hl7.fhir.r4.model.Enumeration tgt = new org.hl7.fhir.r4.model.Enumeration<>(new org.hl7.fhir.r4.model.Device.FHIRDeviceStatusEnumFactory()); + VersionConvertor_30_40.copyElement(src, tgt); + switch (src.getValue()) { + case ACTIVE: + tgt.setValue(org.hl7.fhir.r4.model.Device.FHIRDeviceStatus.ACTIVE); + break; + case INACTIVE: + tgt.setValue(org.hl7.fhir.r4.model.Device.FHIRDeviceStatus.INACTIVE); + break; + case ENTEREDINERROR: + tgt.setValue(org.hl7.fhir.r4.model.Device.FHIRDeviceStatus.ENTEREDINERROR); + break; + case UNKNOWN: + tgt.setValue(org.hl7.fhir.r4.model.Device.FHIRDeviceStatus.UNKNOWN); + break; + default: + tgt.setValue(org.hl7.fhir.r4.model.Device.FHIRDeviceStatus.NULL); + break; + } + return tgt; + } + + static public org.hl7.fhir.dstu3.model.Enumeration convertFHIRDeviceStatus(org.hl7.fhir.r4.model.Enumeration src) throws FHIRException { + if (src == null || src.isEmpty()) + return null; + org.hl7.fhir.dstu3.model.Enumeration tgt = new org.hl7.fhir.dstu3.model.Enumeration<>(new org.hl7.fhir.dstu3.model.Device.FHIRDeviceStatusEnumFactory()); + VersionConvertor_30_40.copyElement(src, tgt); + switch (src.getValue()) { + case ACTIVE: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.FHIRDeviceStatus.ACTIVE); + break; + case INACTIVE: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.FHIRDeviceStatus.INACTIVE); + break; + case ENTEREDINERROR: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.FHIRDeviceStatus.ENTEREDINERROR); + break; + case UNKNOWN: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.FHIRDeviceStatus.UNKNOWN); + break; + default: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.FHIRDeviceStatus.NULL); + break; + } + return tgt; + } + + static public org.hl7.fhir.r4.model.Enumeration convertUDIEntryType(org.hl7.fhir.dstu3.model.Enumeration src) throws FHIRException { + if (src == null || src.isEmpty()) + return null; + org.hl7.fhir.r4.model.Enumeration tgt = new org.hl7.fhir.r4.model.Enumeration<>(new org.hl7.fhir.r4.model.Device.UDIEntryTypeEnumFactory()); + VersionConvertor_30_40.copyElement(src, tgt); + switch (src.getValue()) { + case BARCODE: + tgt.setValue(org.hl7.fhir.r4.model.Device.UDIEntryType.BARCODE); + break; + case RFID: + tgt.setValue(org.hl7.fhir.r4.model.Device.UDIEntryType.RFID); + break; + case MANUAL: + tgt.setValue(org.hl7.fhir.r4.model.Device.UDIEntryType.MANUAL); + break; + case CARD: + tgt.setValue(org.hl7.fhir.r4.model.Device.UDIEntryType.CARD); + break; + case SELFREPORTED: + tgt.setValue(org.hl7.fhir.r4.model.Device.UDIEntryType.SELFREPORTED); + break; + case UNKNOWN: + tgt.setValue(org.hl7.fhir.r4.model.Device.UDIEntryType.UNKNOWN); + break; + default: + tgt.setValue(org.hl7.fhir.r4.model.Device.UDIEntryType.NULL); + break; + } + return tgt; + } + + static public org.hl7.fhir.dstu3.model.Enumeration convertUDIEntryType(org.hl7.fhir.r4.model.Enumeration src) throws FHIRException { + if (src == null || src.isEmpty()) + return null; + org.hl7.fhir.dstu3.model.Enumeration tgt = new org.hl7.fhir.dstu3.model.Enumeration<>(new org.hl7.fhir.dstu3.model.Device.UDIEntryTypeEnumFactory()); + VersionConvertor_30_40.copyElement(src, tgt); + switch (src.getValue()) { + case BARCODE: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.UDIEntryType.BARCODE); + break; + case RFID: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.UDIEntryType.RFID); + break; + case MANUAL: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.UDIEntryType.MANUAL); + break; + case CARD: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.UDIEntryType.CARD); + break; + case SELFREPORTED: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.UDIEntryType.SELFREPORTED); + break; + case UNKNOWN: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.UDIEntryType.UNKNOWN); + break; + default: + tgt.setValue(org.hl7.fhir.dstu3.model.Device.UDIEntryType.NULL); + break; + } + return tgt; + } +} \ No newline at end of file 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.convertors/src/test/java/org/hl7/fhir/convertors/conv30_40/Device30_40Test.java b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv30_40/Device30_40Test.java new file mode 100644 index 000000000..aa617a1e9 --- /dev/null +++ b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/conv30_40/Device30_40Test.java @@ -0,0 +1,43 @@ +package org.hl7.fhir.convertors.conv30_40; + +import org.hl7.fhir.convertors.VersionConvertor_30_40; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; + +public class Device30_40Test { + @Test + @DisplayName("Test r4 -> dstu3 device conversion.") + public void test1() throws IOException { + InputStream r4_input = this.getClass().getResourceAsStream("/0_device_40.json"); + InputStream dstu3_expected_output = this.getClass().getResourceAsStream("/0_device_30.json"); + + org.hl7.fhir.r4.model.Device r4_actual = (org.hl7.fhir.r4.model.Device) new org.hl7.fhir.r4.formats.JsonParser().parse(r4_input); + org.hl7.fhir.dstu3.model.Resource dstu3_conv = VersionConvertor_30_40.convertResource(r4_actual, true); + + org.hl7.fhir.dstu3.formats.JsonParser dstu3_parser = new org.hl7.fhir.dstu3.formats.JsonParser(); + org.hl7.fhir.dstu3.model.Resource dstu3_expected = dstu3_parser.parse(dstu3_expected_output); + + Assertions.assertTrue(dstu3_expected.equalsDeep(dstu3_conv), + "Failed comparing\n" + dstu3_parser.composeString(dstu3_expected) + "\nand\n" + dstu3_parser.composeString(dstu3_conv)); + } + + @Test + @DisplayName("Test r4 -> dstu3 device conversion, part 2.") + public void test2() throws IOException { + InputStream r4_input = this.getClass().getResourceAsStream("/1_device_40.json"); + InputStream dstu3_expected_output = this.getClass().getResourceAsStream("/1_device_30.json"); + + org.hl7.fhir.r4.model.Device r4_actual = (org.hl7.fhir.r4.model.Device) new org.hl7.fhir.r4.formats.JsonParser().parse(r4_input); + org.hl7.fhir.dstu3.model.Resource dstu3_conv = VersionConvertor_30_40.convertResource(r4_actual, true); + + org.hl7.fhir.dstu3.formats.JsonParser dstu3_parser = new org.hl7.fhir.dstu3.formats.JsonParser(); + org.hl7.fhir.dstu3.model.Resource dstu3_expected = dstu3_parser.parse(dstu3_expected_output); + + Assertions.assertTrue(dstu3_expected.equalsDeep(dstu3_conv), + "Failed comparing\n" + dstu3_parser.composeString(dstu3_expected) + "\nand\n" + dstu3_parser.composeString(dstu3_expected)); + } +} \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/test/resources/0_device_30.json b/org.hl7.fhir.convertors/src/test/resources/0_device_30.json new file mode 100644 index 000000000..95555f10d --- /dev/null +++ b/org.hl7.fhir.convertors/src/test/resources/0_device_30.json @@ -0,0 +1,13 @@ +{ + "resourceType": "Device", + "id": "DEV000000000872", + "identifier": [ + { + "system": "https://fresenius.org/device", + "value": "DEV000000000872" + } + ], + "udi": { + "name": "LC021691" + } +} diff --git a/org.hl7.fhir.convertors/src/test/resources/0_device_40.json b/org.hl7.fhir.convertors/src/test/resources/0_device_40.json new file mode 100644 index 000000000..6d3895d27 --- /dev/null +++ b/org.hl7.fhir.convertors/src/test/resources/0_device_40.json @@ -0,0 +1,20 @@ +{ + "resourceType": "Device", + "id": "DEV000000000872", + "identifier": [ + { + "system": "https://fresenius.org/device", + "value": "DEV000000000872" + } + ], + "definition": { + "reference": "Dialysis" + }, + "serialNumber": "LC021691", + "deviceName": [ + { + "name": "LC021691", + "type": "manufacturer-name" + } + ] +} \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/test/resources/0_medication_request_40.json b/org.hl7.fhir.convertors/src/test/resources/0_medication_request_40.json index 325b59a1a..a00906b3e 100644 --- a/org.hl7.fhir.convertors/src/test/resources/0_medication_request_40.json +++ b/org.hl7.fhir.convertors/src/test/resources/0_medication_request_40.json @@ -18,9 +18,7 @@ "dosageInstruction": [{"text": "Take 10 mg by mouth nightly.", "timing": {"repeat": {"boundsPeriod": {"start": "2016-11-15T00:00:00Z", "end": "2016-11-23T00:00:00Z"}, - "count": 0, "frequency": 1, - "frequencyMax": 0, "period": 1.0, "periodUnit": "d"}}, "asNeededBoolean": "False", diff --git a/org.hl7.fhir.convertors/src/test/resources/1_device_30.json b/org.hl7.fhir.convertors/src/test/resources/1_device_30.json new file mode 100644 index 000000000..e06402f5a --- /dev/null +++ b/org.hl7.fhir.convertors/src/test/resources/1_device_30.json @@ -0,0 +1,13 @@ +{ + "resourceType": "Device", + "id": "DEV000000000873", + "identifier": [ + { + "system": "https://fresenius.org/device", + "value": "DEV000000000873" + } + ], + "udi": { + "name": "LC020678" + } +} \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/test/resources/1_device_40.json b/org.hl7.fhir.convertors/src/test/resources/1_device_40.json new file mode 100644 index 000000000..d7bef3a9b --- /dev/null +++ b/org.hl7.fhir.convertors/src/test/resources/1_device_40.json @@ -0,0 +1,20 @@ +{ + "resourceType": "Device", + "id": "DEV000000000873", + "identifier": [ + { + "system": "https://fresenius.org/device", + "value": "DEV000000000873" + } + ], + "definition": { + "reference": "Dialysis" + }, + "serialNumber": "LC020678", + "deviceName": [ + { + "name": "LC020678", + "type": "manufacturer-name" + } + ] +} \ No newline at end of file diff --git a/org.hl7.fhir.core.generator/configuration/ContactDetail.java b/org.hl7.fhir.core.generator/configuration/ContactDetail.java new file mode 100644 index 000000000..f307e2aa0 --- /dev/null +++ b/org.hl7.fhir.core.generator/configuration/ContactDetail.java @@ -0,0 +1,26 @@ + public ContactPoint getEmail() { + for (ContactPoint cp : getTelecom()) { + if (cp.getSystem() == ContactPointSystem.EMAIL) { + return cp; + } + } + return null; + } + + public ContactPoint getPhone() { + for (ContactPoint cp : getTelecom()) { + if (cp.getSystem() == ContactPointSystem.PHONE) { + return cp; + } + } + return null; + } + + public ContactPoint getUrl() { + for (ContactPoint cp : getTelecom()) { + if (cp.getSystem() == ContactPointSystem.URL) { + return cp; + } + } + return null; + } diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index 1fffca93b..171a34fec 100644 --- a/org.hl7.fhir.dstu2/pom.xml +++ b/org.hl7.fhir.dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index dabbcf9fa..d40fdf29a 100644 --- a/org.hl7.fhir.dstu2016may/pom.xml +++ b/org.hl7.fhir.dstu2016may/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index bb3123f5c..978a05efe 100644 --- a/org.hl7.fhir.dstu3/pom.xml +++ b/org.hl7.fhir.dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml 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/pom.xml b/org.hl7.fhir.r4/pom.xml index 1a88adbb5..60bb2e702 100644 --- a/org.hl7.fhir.r4/pom.xml +++ b/org.hl7.fhir.r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml 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..671fc4021 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 @@ -146,23 +146,8 @@ public class FHIRLexer { } public void next() throws FHIRLexerException { + skipWhitespaceAndComments(); current = null; - boolean last13 = false; - while (cursor < source.length() && Character.isWhitespace(source.charAt(cursor))) { - if (source.charAt(cursor) == '\r') { - currentLocation.setLine(currentLocation.getLine() + 1); - currentLocation.setColumn(1); - last13 = true; - } else if (!last13 && (source.charAt(cursor) == '\n')) { - currentLocation.setLine(currentLocation.getLine() + 1); - currentLocation.setColumn(1); - last13 = false; - } else { - last13 = false; - currentLocation.setColumn(currentLocation.getColumn() + 1); - } - cursor++; - } currentStart = cursor; currentStartLocation = currentLocation; if (cursor < source.length()) { @@ -208,9 +193,8 @@ public class FHIRLexer { } else if (ch == '/') { cursor++; if (cursor < source.length() && (source.charAt(cursor) == '/')) { - cursor++; - while (cursor < source.length() && !((source.charAt(cursor) == '\r') || source.charAt(cursor) == '\n')) - cursor++; + // this is en error - should already have been skipped + error("This shoudn't happen?"); } current = source.substring(currentStart, cursor); } else if (ch == '$') { @@ -297,6 +281,33 @@ public class FHIRLexer { } + private void skipWhitespaceAndComments() { + boolean last13 = false; + boolean done = false; + while (cursor < source.length() && !done) { + if (cursor < source.length() -1 && "//".equals(source.substring(cursor, cursor+2))) { + while (cursor < source.length() && !((source.charAt(cursor) == '\r') || source.charAt(cursor) == '\n')) + cursor++; + } else if (cursor < source.length() - 1 && "/*".equals(source.substring(cursor, cursor+2))) { + while (cursor < source.length() - 1 && !"*/".equals(source.substring(cursor, cursor+2))) { + last13 = currentLocation.checkChar(source.charAt(cursor), last13); + cursor++; + } + if (cursor >= source.length() -1) { + error("Unfinished comment"); + } else { + cursor = cursor + 2; + } + } else if (Character.isWhitespace(source.charAt(cursor))) { + last13 = currentLocation.checkChar(source.charAt(cursor), last13); + cursor++; + } else { + done = true; + } + } + } + + private boolean isDateChar(char ch,int start) { int eot = source.charAt(start+1) == 'T' ? 10 : 20; 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..31701df2f 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; @@ -232,7 +233,7 @@ public class FHIRPathEngine { * @param parameters * @return */ - public List executeFunction(Object appContext, String functionName, List> parameters); + public List executeFunction(Object appContext, List focus, String functionName, List> parameters); /** * Implementation of resolve() function. Passed a string, return matching resource, if one is known - else null @@ -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; } @@ -2785,7 +2858,7 @@ public class FHIRPathEngine { List> params = new ArrayList>(); for (ExpressionNode p : exp.getParameters()) params.add(execute(context, focus, p, true)); - return hostServices.executeFunction(context.appInfo, exp.getName(), params); + return hostServices.executeFunction(context.appInfo, focus, exp.getName(), params); } default: throw new Error("not Implemented yet"); @@ -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/LiquidEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/LiquidEngine.java index 008d857eb..6a307b8ca 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/LiquidEngine.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/LiquidEngine.java @@ -405,11 +405,11 @@ public class LiquidEngine implements IEvaluationContext { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.executeFunction(ctxt.externalContext, functionName, parameters); + return externalHostServices.executeFunction(ctxt.externalContext, focus, functionName, parameters); } @Override 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/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java index a38603abe..ae4524927 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java @@ -202,7 +202,7 @@ public class StructureMapUtilities { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { throw new Error("Not Implemented Yet"); } 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..357f8e143 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 @@ -64,7 +64,7 @@ public class FHIRPathTests { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.executeFunction), when item is element"); } @@ -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.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java index caef44031..747c66149 100644 --- a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java @@ -316,7 +316,7 @@ public class SnapShotGenerationTests { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { if ("fixture".equals(functionName)) { String id = fp.convertToString(parameters.get(0)); Resource res = fetchFixture(id); diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index 1af8b5847..3cc483033 100644 --- a/org.hl7.fhir.r5/pom.xml +++ b/org.hl7.fhir.r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java index ceb3a3f75..43d18b3d2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java @@ -242,7 +242,7 @@ public class ComparisonRenderer implements IEvaluationContext { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { return null; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index 970bac706..fbb99dd77 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -3245,14 +3245,22 @@ public class ProfileUtilities extends TranslatingUtilities { tl = t; if (t.hasTarget()) { c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null)); + if (isMustSupportDirect(t) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } c.getPieces().add(gen.new Piece(null, "(", null)); boolean tfirst = true; - for (UriType u : t.getTargetProfile()) { + for (CanonicalType u : t.getTargetProfile()) { if (tfirst) tfirst = false; else c.addPiece(gen.new Piece(null, " | ", null)); genTargetLink(gen, profileBaseFileName, corePath, c, t, u.getValue()); + if (isMustSupport(u) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); + } } c.getPieces().add(gen.new Piece(null, ")", null)); if (t.getAggregation().size() > 0) { @@ -3289,6 +3297,10 @@ public class ProfileUtilities extends TranslatingUtilities { } } else c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath)? corePath: "")+ref, t.getWorkingCode(), null))); + if (isMustSupport(p) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); + } } } else { String tc = t.getWorkingCode(); @@ -3301,8 +3313,13 @@ public class ProfileUtilities extends TranslatingUtilities { } } else if (pkp != null && pkp.hasLinkFor(tc)) { c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null))); - } else + } else { c.addPiece(checkForNoChange(t, gen.new Piece(null, tc, null))); + } + if (isMustSupportDirect(t) && e.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } } } return c; @@ -3812,7 +3829,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (element != null && element.getIsSummary()) { checkForNoChange(element.getIsSummaryElement(), gc.addStyledText(translate("sd.table", "This element is included in summaries"), "\u03A3", null, null, null, false)); } - if (element != null && (!element.getConstraint().isEmpty() || !element.getCondition().isEmpty())) { + if (element != null && (hasNonBaseConstraints(element.getConstraint()) || hasNonBaseConditions(element.getCondition()))) { gc.addStyledText(translate("sd.table", "This element has or is affected by some invariants ("+listConstraintsAndConditions(element)+")"), "I", null, null, null, false); } @@ -3860,7 +3877,8 @@ public class ProfileUtilities extends TranslatingUtilities { return res; } - private Cell addCell(Row row, Cell cell) { + + private Cell addCell(Row row, Cell cell) { row.getCells().add(cell); return (cell); } @@ -3869,18 +3887,49 @@ public class ProfileUtilities extends TranslatingUtilities { return app == null ? src : src + app; } + private boolean hasNonBaseConditions(List conditions) { + for (IdType c : conditions) { + if (!isBaseCondition(c)) { + return true; + } + } + return false; + } + + + private boolean hasNonBaseConstraints(List constraints) { + for (ElementDefinitionConstraintComponent c : constraints) { + if (!isBaseConstraint(c)) { + return true; + } + } + return false; + } private String listConstraintsAndConditions(ElementDefinition element) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); for (ElementDefinitionConstraintComponent con : element.getConstraint()) { - b.append(con.getKey()); + if (!isBaseConstraint(con)) { + b.append(con.getKey()); + } } for (IdType id : element.getCondition()) { - b.append(id.asStringValue()); + if (!isBaseCondition(id)) { + b.append(id.asStringValue()); + } } return b.toString(); } + private boolean isBaseCondition(IdType c) { + String key = c.asStringValue(); + return key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); + } + + private boolean isBaseConstraint(ElementDefinitionConstraintComponent con) { + String key = con.getKey(); + return key.startsWith("ele-") || key.startsWith("res-") || key.startsWith("ext-") || key.startsWith("dom-") || key.startsWith("dr-"); + } private void makeChoiceRows(List subRows, ElementDefinition element, HierarchicalTableGenerator gen, String corePath, String profileBaseFileName) { // create a child for each choice @@ -3899,6 +3948,10 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(gen.new Piece(corePath+"datatypes.html#canonical", "canonical", null)); else c.getPieces().add(gen.new Piece(corePath+"references.html#Reference", "Reference", null)); + if (isMustSupportDirect(tr) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } c.getPieces().add(gen.new Piece(null, "(", null)); } boolean first = true; @@ -3906,6 +3959,10 @@ public class ProfileUtilities extends TranslatingUtilities { if (!first) c.getPieces().add(gen.new Piece(null, " | ", null)); genTargetLink(gen, profileBaseFileName, corePath, c, tr, rt.getValue()); + if (isMustSupport(rt) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false); + } first = false; } if (first) @@ -3924,20 +3981,23 @@ public class ProfileUtilities extends TranslatingUtilities { choicerow.getCells().add(gen.new Cell()); choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); choicerow.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); - choicerow.getCells().add(gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getType(), null, null)); - // } else if (definitions.getConstraints().contthnsKey(t)) { - // ProfiledType pt = definitions.getConstraints().get(t); - // choicerow.getCells().add(gen.new Cell(null, null, e.getName().replace("[x]", Utilities.capitalize(pt.getBaseType())), definitions.getTypes().containsKey(t) ? definitions.getTypes().get(t).getDefinition() : null, null)); - // choicerow.getCells().add(gen.new Cell()); - // choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - // choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); - // choicerow.getCells().add(gen.new Cell(null, definitions.getSrcFile(t)+".html#"+t.replace("*", "open"), t, null, null)); + Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getType(), null, null); + choicerow.getCells().add(c); + if (isMustSupport(tr) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } } else { choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); choicerow.getCells().add(gen.new Cell()); choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); - choicerow.getCells().add(gen.new Cell(null, pkp.getLinkFor(corePath, t), sd.getType(), null, null)); + Cell c = gen.new Cell(null, pkp.getLinkFor(corePath, t), sd.getType(), null, null); + choicerow.getCells().add(c); + if (isMustSupport(tr) && element.getMustSupport()) { + c.addPiece(gen.new Piece(null, " ", null)); + c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false); + } } if (tr.hasProfile()) { Cell typeCell = choicerow.getCells().get(3); @@ -3950,6 +4010,10 @@ public class ProfileUtilities extends TranslatingUtilities { typeCell.addPiece(gen.new Piece(null, "?gen-e2?", null)); else typeCell.addPiece(gen.new Piece(psd.getUserString("path"), psd.getName(), psd.present())); + if (isMustSupport(pt) && element.getMustSupport()) { + typeCell.addPiece(gen.new Piece(null, " ", null)); + typeCell.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false); + } } typeCell.addPiece(gen.new Piece(null, ")", null)); @@ -6145,6 +6209,32 @@ public class ProfileUtilities extends TranslatingUtilities { return grp; } + public static boolean isMustSupportDirect(TypeRefComponent tr) { + return ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))); + } + public static boolean isMustSupport(TypeRefComponent tr) { + if ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))) { + return true; + } + if (isMustSupport(tr.getProfile())) { + return true; + } + return isMustSupport(tr.getTargetProfile()); + } + + public static boolean isMustSupport(List profiles) { + for (CanonicalType ct : profiles) { + if (isMustSupport(ct)) { + return true; + } + } + return false; + } + + + public static boolean isMustSupport(CanonicalType profile) { + return "true".equals(ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_MUST_SUPPORT)); + } } \ No newline at end of file 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/ContactDetail.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ContactDetail.java index 59e3195d8..2dc6dfa16 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ContactDetail.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ContactDetail.java @@ -35,6 +35,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem; import org.hl7.fhir.r5.model.Enumerations.*; import org.hl7.fhir.instance.model.api.IBaseDatatypeElement; import org.hl7.fhir.exceptions.FHIRException; @@ -308,6 +309,32 @@ public class ContactDetail extends DataType implements ICompositeType { return super.isEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(name, telecom); } + public ContactPoint getEmail() { + for (ContactPoint cp : getTelecom()) { + if (cp.getSystem() == ContactPointSystem.EMAIL) { + return cp; + } + } + return null; + } + + public ContactPoint getPhone() { + for (ContactPoint cp : getTelecom()) { + if (cp.getSystem() == ContactPointSystem.PHONE) { + return cp; + } + } + return null; + } + + public ContactPoint getUrl() { + for (ContactPoint cp : getTelecom()) { + if (cp.getSystem() == ContactPointSystem.URL) { + return cp; + } + } + return null; + } } 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/BundleRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BundleRenderer.java index 735b139dd..1817f6994 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BundleRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BundleRenderer.java @@ -43,7 +43,9 @@ public class BundleRenderer extends ResourceRenderer { @Override public boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { - return render(x, (Bundle) r); + XhtmlNode n = render((Bundle) r); + x.addChildren(n.getChildNodes()); + return false; } @Override diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java index c6abb1b4a..7af63b922 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java @@ -134,6 +134,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { return false; } XhtmlNode t = x.table( "codes"); + boolean definitions = false; boolean commentS = false; boolean deprecated = false; boolean display = false; @@ -161,14 +162,15 @@ public class CodeSystemRenderer extends TerminologyRenderer { display = display || conceptsHaveDisplay(c); version = version || conceptsHaveVersion(c); hierarchy = hierarchy || c.hasConcept(); + definitions = definitions || conceptsHaveDefinition(c); } CodeSystemNavigator csNav = new CodeSystemNavigator(cs); hierarchy = hierarchy || csNav.isRestructure(); List langs = new ArrayList<>(); - addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, true, commentS, version, deprecated, properties, null, false), maps); + addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, definitions, commentS, version, deprecated, properties, null, false), maps); for (ConceptDefinitionComponent c : csNav.getConcepts(null)) { - hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs) || hasExtensions; + hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, definitions, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs) || hasExtensions; } if (langs.size() > 0) { Collections.sort(langs); @@ -185,6 +187,23 @@ public class CodeSystemRenderer extends TerminologyRenderer { return hasExtensions; } + private boolean conceptsHaveDefinition(ConceptDefinitionComponent c) { + if (c.hasDefinition()) { + return true; + } + for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { + if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { + return true; + } + } + for (ConceptDefinitionComponent g : c.getConcept()) { + if (conceptsHaveDefinition(g)) { + return true; + } + } + return false; + } + private boolean conceptsHaveProperty(ConceptDefinitionComponent c, PropertyComponent cp) { if (CodeSystemUtilities.hasProperty(c, cp.getCode())) return true; @@ -271,7 +290,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { - private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, boolean comment, boolean version, boolean deprecated, List maps, String system, CodeSystem cs, List properties, CodeSystemNavigator csNav, List langs) throws FHIRFormatError, DefinitionException, IOException { + private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, boolean hasDefinitions, boolean comment, boolean version, boolean deprecated, List maps, String system, CodeSystem cs, List properties, CodeSystemNavigator csNav, List langs) throws FHIRFormatError, DefinitionException, IOException { boolean hasExtensions = false; XhtmlNode tr = t.tr(); XhtmlNode td = tr.td(); @@ -297,36 +316,38 @@ public class CodeSystemRenderer extends TerminologyRenderer { if (hasDisplay) { td = tr.td(); renderDisplayName(c, cs, td); - } - td = tr.td(); - if (c != null && - c.hasDefinitionElement()) { - if (getContext().getLang() == null) { - if (hasMarkdownInDefinitions(cs)) - addMarkdown(td, c.getDefinition()); - else + } + if (hasDefinitions) { + td = tr.td(); + if (c != null && + c.hasDefinitionElement()) { + if (getContext().getLang() == null) { + if (hasMarkdownInDefinitions(cs)) + addMarkdown(td, c.getDefinition()); + else + td.addText(c.getDefinition()); + } else if (getContext().getLang().equals("*")) { + boolean sl = false; + for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) + if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) + sl = true; + td.addText((sl ? cs.getLanguage("en")+": " : "")+c.getDefinition()); + for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { + if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { + td.br(); + td.addText(cd.getLanguage()+": "+cd.getValue()); + } + } + } else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) { td.addText(c.getDefinition()); - } else if (getContext().getLang().equals("*")) { - boolean sl = false; - for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) - if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) - sl = true; - td.addText((sl ? cs.getLanguage("en")+": " : "")+c.getDefinition()); - for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { - if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { - td.br(); - td.addText(cd.getLanguage()+": "+cd.getValue()); + } else { + for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { + if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && cd.getLanguage().equals(getContext().getLang())) { + td.addText(cd.getValue()); + } } } - } else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) { - td.addText(c.getDefinition()); - } else { - for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { - if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && cd.getLanguage().equals(getContext().getLang())) { - td.addText(cd.getValue()); - } - } - } + } } if (deprecated) { td = tr.td(); @@ -395,15 +416,17 @@ public class CodeSystemRenderer extends TerminologyRenderer { for (PropertyComponent pc : properties) { td = tr.td(); boolean first = true; - ConceptPropertyComponent pcv = CodeSystemUtilities.getProperty(c, pc.getCode()); - if (pcv != null && pcv.hasValue()) { - if (first) first = false; else td.addText(", "); - if (pcv.hasValueCoding()) { - td.addText(pcv.getValueCoding().getCode()); - } else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrl(pcv.getValue().primitiveValue())) { - td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue()); - } else { - td.addText(pcv.getValue().primitiveValue()); + List pcvl = CodeSystemUtilities.getPropertyValues(c, pc.getCode()); + for (ConceptPropertyComponent pcv : pcvl) { + if (pcv.hasValue()) { + if (first) first = false; else td.addText(", "); + if (pcv.hasValueCoding()) { + td.addText(pcv.getValueCoding().getCode()); + } else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrl(pcv.getValue().primitiveValue())) { + td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue()); + } else { + td.addText(pcv.getValue().primitiveValue()); + } } } } @@ -427,7 +450,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { } List ocl = csNav.getOtherChildren(c); for (ConceptDefinitionComponent cc : csNav.getConcepts(c)) { - hasExtensions = addDefineRowToTable(t, cc, level+1, hasHierarchy, hasDisplay, comment, version, deprecated, maps, system, cs, properties, csNav, langs) || hasExtensions; + hasExtensions = addDefineRowToTable(t, cc, level+1, hasHierarchy, hasDisplay, hasDefinitions, comment, version, deprecated, maps, system, cs, properties, csNav, langs) || hasExtensions; } for (ConceptDefinitionComponent cc : ocl) { tr = t.tr(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java index b13bc6fd1..2033956f9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java @@ -168,6 +168,10 @@ public class DataRenderer extends Renderer { } protected String describeLang(String lang) { + // special cases: + if ("fr-CA".equals(lang)) { + return "French (Canadian)"; // this one was omitted from the value set + } ValueSet v = getContext().getWorker().fetchResource(ValueSet.class, "http://hl7.org/fhir/ValueSet/languages"); if (v != null) { ConceptReferenceComponent l = null; @@ -176,11 +180,22 @@ public class DataRenderer extends Renderer { l = cc; } if (l == null) { - if (lang.contains("-")) + if (lang.contains("-")) { lang = lang.substring(0, lang.indexOf("-")); + } for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) { - if (cc.getCode().equals(lang) || cc.getCode().startsWith(lang+"-")) + if (cc.getCode().equals(lang)) { l = cc; + break; + } + } + if (l == null) { + for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) { + if (cc.getCode().startsWith(lang+"-")) { + l = cc; + break; + } + } } } if (l != null) { @@ -236,12 +251,12 @@ public class DataRenderer extends Renderer { } else { return "No display for "+b.fhirType(); } - } public String display(DataType type) { - if (type.isEmpty()) + if (type == null || type.isEmpty()) { return ""; + } if (type instanceof Coding) { return displayCoding((Coding) type); @@ -339,7 +354,6 @@ public class DataRenderer extends Renderer { } else { x.tx("No display for "+type.fhirType()); } - } private void renderReference(XhtmlNode x, Reference ref) { @@ -353,15 +367,17 @@ public class DataRenderer extends Renderer { } public void renderDateTime(XhtmlNode x, Base e) { - if (e.hasPrimitiveValue()) + if (e.hasPrimitiveValue()) { x.addText(((DateTimeType) e).toHumanDisplay()); + } } protected void renderUri(XhtmlNode x, UriType uri) { - if (uri.getValue().startsWith("mailto:")) + if (uri.getValue().startsWith("mailto:")) { x.ah(uri.getValue()).addText(uri.getValue().substring(7)); - else + } else { x.ah(uri.getValue()).addText(uri.getValue()); + } } protected void renderUri(XhtmlNode x, UriType uri, String path, String id) { @@ -684,9 +700,63 @@ public class DataRenderer extends Renderer { } protected void renderContactPoint(XhtmlNode x, ContactPoint contact) { - x.addText(displayContactPoint(contact)); + if (contact != null) { + if (!contact.hasSystem()) { + x.addText(displayContactPoint(contact)); + } else { + switch (contact.getSystem()) { + case EMAIL: + x.ah("mailto:"+contact.getValue()).tx(contact.getValue()); + break; + case FAX: + x.addText(displayContactPoint(contact)); + break; + case NULL: + x.addText(displayContactPoint(contact)); + break; + case OTHER: + x.addText(displayContactPoint(contact)); + break; + case PAGER: + x.addText(displayContactPoint(contact)); + break; + case PHONE: + if (contact.hasValue() && contact.getValue().startsWith("+")) { + x.ah("tel:"+contact.getValue().replace(" ", "")).tx(contact.getValue()); + } else { + x.addText(displayContactPoint(contact)); + } + break; + case SMS: + x.addText(displayContactPoint(contact)); + break; + case URL: + x.ah(contact.getValue()).tx(contact.getValue()); + break; + default: + break; + } + } + } } + protected void displayContactPoint(XhtmlNode p, ContactPoint c) { + if (c != null) { + if (c.getSystem() == ContactPointSystem.PHONE) { + p.tx("Phone: "+c.getValue()); + } else if (c.getSystem() == ContactPointSystem.FAX) { + p.tx("Fax: "+c.getValue()); + } else if (c.getSystem() == ContactPointSystem.EMAIL) { + p.tx(c.getValue()); + } else if (c.getSystem() == ContactPointSystem.URL) { + if (c.getValue().length() > 30) { + p.addText(c.getValue().substring(0, 30)+"..."); + } else { + p.addText(c.getValue()); + } + } + } + } protected void addTelecom(XhtmlNode p, ContactPoint c) { if (c.getSystem() == ContactPointSystem.PHONE) { @@ -713,7 +783,6 @@ public class DataRenderer extends Renderer { } } - protected String displayQuantity(Quantity q) { StringBuilder s = new StringBuilder(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/LibraryRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/LibraryRenderer.java new file mode 100644 index 000000000..7173349a3 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/LibraryRenderer.java @@ -0,0 +1,533 @@ +package org.hl7.fhir.r5.renderers; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.List; + +import org.apache.commons.codec.binary.Base64; +import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.model.Annotation; +import org.hl7.fhir.r5.model.Attachment; +import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.ContactDetail; +import org.hl7.fhir.r5.model.ContactPoint; +import org.hl7.fhir.r5.model.DataRequirement; +import org.hl7.fhir.r5.model.DomainResource; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.ListResource; +import org.hl7.fhir.r5.model.ListResource.ListResourceEntryComponent; +import org.hl7.fhir.r5.model.ParameterDefinition; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; +import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper; +import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; +import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; +import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceWithReference; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; + +public class LibraryRenderer extends ResourceRenderer { + + private static final int DATA_IMG_SIZE_CUTOFF = 4000; + + public LibraryRenderer(RenderingContext context) { + super(context); + } + + public LibraryRenderer(RenderingContext context, ResourceContext rcontext) { + super(context, rcontext); + } + + public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException { + return render(x, (Library) dr); + } + + public boolean render(XhtmlNode x, ResourceWrapper lib) throws FHIRFormatError, DefinitionException, IOException { + PropertyWrapper authors = lib.getChildByName("author"); + PropertyWrapper editors = lib.getChildByName("editor"); + PropertyWrapper reviewers = lib.getChildByName("reviewer"); + PropertyWrapper endorsers = lib.getChildByName("endorser"); + if ((authors != null && authors.hasValues()) || (editors != null && editors.hasValues()) || (reviewers != null && reviewers.hasValues()) || (endorsers != null && endorsers.hasValues())) { + boolean email = hasCT(authors, "email") || hasCT(editors, "email") || hasCT(reviewers, "email") || hasCT(endorsers, "email"); + boolean phone = hasCT(authors, "phone") || hasCT(editors, "phone") || hasCT(reviewers, "phone") || hasCT(endorsers, "phone"); + boolean url = hasCT(authors, "url") || hasCT(editors, "url") || hasCT(reviewers, "url") || hasCT(endorsers, "url"); + x.h2().tx("Participants"); + XhtmlNode t = x.table("grid"); + if (authors != null) { + for (BaseWrapper cd : authors.getValues()) { + participantRow(t, "Author", cd, email, phone, url); + } + } + if (authors != null) { + for (BaseWrapper cd : editors.getValues()) { + participantRow(t, "Editor", cd, email, phone, url); + } + } + if (authors != null) { + for (BaseWrapper cd : reviewers.getValues()) { + participantRow(t, "Reviewer", cd, email, phone, url); + } + } + if (authors != null) { + for (BaseWrapper cd : endorsers.getValues()) { + participantRow(t, "Endorser", cd, email, phone, url); + } + } + } + PropertyWrapper artifacts = lib.getChildByName("relatedArtifact"); + if (artifacts != null && artifacts.hasValues()) { + x.h2().tx("Related Artifacts"); + XhtmlNode t = x.table("grid"); + boolean label = false; + boolean display = false; + boolean citation = false; + for (BaseWrapper ra : artifacts.getValues()) { + label = label || ra.has("label"); + display = display || ra.has("display"); + citation = citation || ra.has("citation"); + } + for (BaseWrapper ra : artifacts.getValues()) { + renderArtifact(t, ra, lib, label, display, citation); + } + } + PropertyWrapper parameters = lib.getChildByName("parameter"); + if (parameters != null && parameters.hasValues()) { + x.h2().tx("Parameters"); + XhtmlNode t = x.table("grid"); + boolean doco = false; + for (BaseWrapper p : parameters.getValues()) { + doco = doco || p.has("documentation"); + } + for (BaseWrapper p : parameters.getValues()) { + renderParameter(t, p, doco); + } + } + PropertyWrapper dataRequirements = lib.getChildByName("dataRequirement"); + if (dataRequirements != null && dataRequirements.hasValues()) { + x.h2().tx("Data Requirements"); + for (BaseWrapper p : dataRequirements.getValues()) { + renderDataRequirement(x, (DataRequirement) p.getBase()); + } + } + PropertyWrapper contents = lib.getChildByName("content"); + if (contents != null) { + x.h2().tx("Contents"); + boolean isCql = false; + int counter = 0; + for (BaseWrapper p : contents.getValues()) { + Attachment att = (Attachment) p.getBase(); + renderAttachment(x, att, isCql, counter, lib.getId()); + isCql = isCql || (att.hasContentType() && att.getContentType().startsWith("text/cql")); + counter++; + } + } + return false; + } + + private boolean hasCT(PropertyWrapper prop, String type) throws UnsupportedEncodingException, FHIRException, IOException { + if (prop != null) { + for (BaseWrapper cd : prop.getValues()) { + PropertyWrapper telecoms = cd.getChildByName("telecom"); + if (getContactPoint(telecoms, type) != null) { + return true; + } + } + } + return false; + } + + private boolean hasCT(List list, String type) { + for (ContactDetail cd : list) { + for (ContactPoint t : cd.getTelecom()) { + if (type.equals(t.getSystem().toCode())) { + return true; + } + } + } + return false; + } + + + public boolean render(XhtmlNode x, Library lib) throws FHIRFormatError, DefinitionException, IOException { + if (lib.hasAuthor() || lib.hasEditor() || lib.hasReviewer() || lib.hasEndorser()) { + boolean email = hasCT(lib.getAuthor(), "email") || hasCT(lib.getEditor(), "email") || hasCT(lib.getReviewer(), "email") || hasCT(lib.getEndorser(), "email"); + boolean phone = hasCT(lib.getAuthor(), "phone") || hasCT(lib.getEditor(), "phone") || hasCT(lib.getReviewer(), "phone") || hasCT(lib.getEndorser(), "phone"); + boolean url = hasCT(lib.getAuthor(), "url") || hasCT(lib.getEditor(), "url") || hasCT(lib.getReviewer(), "url") || hasCT(lib.getEndorser(), "url"); + x.h2().tx("Participants"); + XhtmlNode t = x.table("grid"); + for (ContactDetail cd : lib.getAuthor()) { + participantRow(t, "Author", cd, email, phone, url); + } + for (ContactDetail cd : lib.getEditor()) { + participantRow(t, "Editor", cd, email, phone, url); + } + for (ContactDetail cd : lib.getReviewer()) { + participantRow(t, "Reviewer", cd, email, phone, url); + } + for (ContactDetail cd : lib.getEndorser()) { + participantRow(t, "Endorser", cd, email, phone, url); + } + } + if (lib.hasRelatedArtifact()) { + x.h2().tx("Related Artifacts"); + XhtmlNode t = x.table("grid"); + boolean label = false; + boolean display = false; + boolean citation = false; + for (RelatedArtifact ra : lib.getRelatedArtifact()) { + label = label || ra.hasLabel(); + display = display || ra.hasDisplay(); + citation = citation || ra.hasCitation(); + } + for (RelatedArtifact ra : lib.getRelatedArtifact()) { + renderArtifact(t, ra, lib, label, display, citation); + } + } + if (lib.hasParameter()) { + x.h2().tx("Parameters"); + XhtmlNode t = x.table("grid"); + boolean doco = false; + for (ParameterDefinition p : lib.getParameter()) { + doco = doco || p.hasDocumentation(); + } + for (ParameterDefinition p : lib.getParameter()) { + renderParameter(t, p, doco); + } + } + if (lib.hasDataRequirement()) { + x.h2().tx("Data Requirements"); + for (DataRequirement p : lib.getDataRequirement()) { + renderDataRequirement(x, p); + } + } + if (lib.hasContent()) { + x.h2().tx("Contents"); + boolean isCql = false; + int counter = 0; + for (Attachment att : lib.getContent()) { + renderAttachment(x, att, isCql, counter, lib.getId()); + isCql = isCql || (att.hasContentType() && att.getContentType().startsWith("text/cql")); + counter++; + } + } + return false; + } + + private void renderParameter(XhtmlNode t, BaseWrapper p, boolean doco) throws UnsupportedEncodingException, FHIRException, IOException { + XhtmlNode tr = t.tr(); + tr.td().tx(p.has("name") ? p.get("name").primitiveValue() : null); + tr.td().tx(p.has("use") ? p.get("use").primitiveValue() : null); + tr.td().tx(p.has("min") ? p.get("min").primitiveValue() : null); + tr.td().tx(p.has("max") ? p.get("max").primitiveValue() : null); + tr.td().tx(p.has("type") ? p.get("type").primitiveValue() : null); + if (doco) { + tr.td().tx(p.has("documentation") ? p.get("documentation").primitiveValue() : null); + } + } + + private void renderParameter(XhtmlNode t, ParameterDefinition p, boolean doco) { + XhtmlNode tr = t.tr(); + tr.td().tx(p.getName()); + tr.td().tx(p.getUse().getDisplay()); + tr.td().tx(p.getMin()); + tr.td().tx(p.getMax()); + tr.td().tx(p.getType().getDisplay()); + if (doco) { + tr.td().tx(p.getDocumentation()); + } + } + + private void renderArtifact(XhtmlNode t, BaseWrapper ra, ResourceWrapper lib, boolean label, boolean display, boolean citation) throws UnsupportedEncodingException, FHIRException, IOException { + XhtmlNode tr = t.tr(); + tr.td().tx(ra.has("type") ? ra.get("type").primitiveValue() : null); + if (label) { + tr.td().tx(ra.has("label") ? ra.get("label").primitiveValue() : null); + } + if (display) { + tr.td().tx(ra.has("display") ? ra.get("display").primitiveValue() : null); + } + if (citation) { + tr.td().markdown(ra.has("citation") ? ra.get("citation").primitiveValue() : null, "Citation"); + } + if (ra.has("resource")) { + renderCanonical(lib, tr.td(), ra.get("resource").primitiveValue()); + } else { + tr.td().tx(ra.has("url") ? ra.get("url").primitiveValue() : null); + } + } + + private void renderArtifact(XhtmlNode t, RelatedArtifact ra, Resource lib, boolean label, boolean display, boolean citation) throws IOException { + XhtmlNode tr = t.tr(); + tr.td().tx(ra.getType().getDisplay()); + if (label) { + tr.td().tx(ra.getLabel()); + } + if (display) { + tr.td().tx(ra.getDisplay()); + } + if (citation) { + tr.td().markdown(ra.getCitation(), "Citation"); + } + if (ra.hasResource()) { + renderCanonical(lib, tr.td(), ra.getResource()); + } else { + tr.td().tx(ra.getUrl()); + } + } + + private void participantRow(XhtmlNode t, String label, BaseWrapper cd, boolean email, boolean phone, boolean url) throws UnsupportedEncodingException, FHIRException, IOException { + XhtmlNode tr = t.tr(); + tr.td().tx(label); + tr.td().tx(cd.get("name") != null ? cd.get("name").primitiveValue() : null); + PropertyWrapper telecoms = cd.getChildByName("telecom"); + if (email) { + renderContactPoint(tr.td(), getContactPoint(telecoms, "email")); + } + if (phone) { + renderContactPoint(tr.td(), getContactPoint(telecoms, "phone")); + } + if (url) { + renderContactPoint(tr.td(), getContactPoint(telecoms, "url")); + } + } + + private ContactPoint getContactPoint(PropertyWrapper telecoms, String value) throws UnsupportedEncodingException, FHIRException, IOException { + for (BaseWrapper t : telecoms.getValues()) { + if (t.has("system")) { + String system = t.get("system").primitiveValue(); + if (value.equals(system)) { + return (ContactPoint) t.getBase(); + } + } + } + return null; + } + + private void participantRow(XhtmlNode t, String label, ContactDetail cd, boolean email, boolean phone, boolean url) { + XhtmlNode tr = t.tr(); + tr.td().tx(label); + tr.td().tx(cd.getName()); + if (email) { + renderContactPoint(tr.td(), cd.getEmail()); + } + if (phone) { + renderContactPoint(tr.td(), cd.getPhone()); + } + if (url) { + renderContactPoint(tr.td(), cd.getUrl()); + } + } + + public void describe(XhtmlNode x, Library lib) { + x.tx(display(lib)); + } + + public String display(Library lib) { + return lib.present(); + } + + @Override + public String display(Resource r) throws UnsupportedEncodingException, IOException { + return ((Library) r).present(); + } + + @Override + public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException { + if (r.has("title")) { + return r.children("title").get(0).getBase().primitiveValue(); + } + return "??"; + } + + private void renderAttachment(XhtmlNode x, Attachment att, boolean noShowData, int counter, String baseId) { + boolean ref = !att.hasData() && att.hasUrl(); + if (ref) { + XhtmlNode p = x.para(); + if (att.hasTitle()) { + p.tx(att.getTitle()); + p.tx(": "); + } + p.code().ah(att.getUrl()).tx(att.getUrl()); + p.tx(" ("); + p.code().tx(att.getContentType()); + p.tx(lang(att)); + p.tx(")"); + } else if (!att.hasData()) { + XhtmlNode p = x.para(); + if (att.hasTitle()) { + p.tx(att.getTitle()); + p.tx(": "); + } + p.code().tx("No Content"); + p.tx(" ("); + p.code().tx(att.getContentType()); + p.tx(lang(att)); + p.tx(")"); + } else { + String txt = getText(att); + if (isImage(att.getContentType())) { + XhtmlNode p = x.para(); + if (att.hasTitle()) { + p.tx(att.getTitle()); + p.tx(": ("); + p.code().tx(att.getContentType()); + p.tx(lang(att)); + p.tx(")"); + } + else { + p.code().tx(att.getContentType()+lang(att)); + } + if (att.getData().length < LibraryRenderer.DATA_IMG_SIZE_CUTOFF) { + x.img("data: "+att.getContentType()+">;base64,"+b64(att.getData())); + } else { + String filename = "Library-"+baseId+(counter == 0 ? "" : "-"+Integer.toString(counter))+"."+imgExtension(att.getContentType()); + x.img(filename); + } + } else if (txt != null && !noShowData) { + XhtmlNode p = x.para(); + if (att.hasTitle()) { + p.tx(att.getTitle()); + p.tx(": ("); + p.code().tx(att.getContentType()); + p.tx(lang(att)); + p.tx(")"); + } + else { + p.code().tx(att.getContentType()+lang(att)); + } + String prismCode = determinePrismCode(att); + if (prismCode != null) { + x.pre().code().setAttribute("class", "language-"+prismCode).tx(txt); + } else { + x.pre().code().tx(txt); + } + } else { + XhtmlNode p = x.para(); + if (att.hasTitle()) { + p.tx(att.getTitle()); + p.tx(": "); + } + p.code().tx("Content not shown - ("); + p.code().tx(att.getContentType()); + p.tx(lang(att)); + p.tx(", size = "+Utilities.describeSize(att.getData().length)+")"); + } + } + } + + private String imgExtension(String contentType) { + if (contentType != null && contentType.startsWith("image/")) { + if (contentType.startsWith("image/png")) { + return "png"; + } + if (contentType.startsWith("image/jpeg")) { + return "jpg"; + } + } + return null; + } + + private String b64(byte[] data) { + byte[] encodeBase64 = Base64.encodeBase64(data); + return new String(encodeBase64); + } + + private boolean isImage(String contentType) { + return imgExtension(contentType) != null; + } + + private String lang(Attachment att) { + if (att.hasLanguage()) { + return ", language = "+describeLang(att.getLanguage()); + } + return ""; + } + + private String getText(Attachment att) { + try { + try { + String src = new String(att.getData(), "UTF-8"); + if (checkString(src)) { + return src; + } + } catch (Exception e) { + // ignore + } + try { + String src = new String(att.getData(), "UTF-16"); + if (checkString(src)) { + return src; + } + } catch (Exception e) { + // ignore + } + try { + String src = new String(att.getData(), "ASCII"); + if (checkString(src)) { + return src; + } + } catch (Exception e) { + // ignore + } + return null; + } catch (Exception e) { + return null; + } + } + + public boolean checkString(String src) { + for (char ch : src.toCharArray()) { + if (ch < ' ' && ch != '\r' && ch != '\n' && ch != '\t') { + return false; + } + } + return true; + } + + private String determinePrismCode(Attachment att) { + if (att.hasContentType()) { + String ct = att.getContentType(); + if (ct.contains(";")) { + ct = ct.substring(0, ct.indexOf(";")); + } + switch (ct) { + case "text/html" : return "html"; + case "text/xml" : return "xml"; + case "application/xml" : return "xml"; + case "text/markdown" : return "markdown"; + case "application/js" : return "JavaScript"; + case "application/css" : return "css"; + case "text/x-csrc" : return "c"; + case "text/x-csharp" : return "csharp"; + case "text/x-c++src" : return "cpp"; + case "application/graphql" : return "graphql"; + case "application/x-java" : return "java"; + case "application/json" : return "json"; + case "text/json" : return "json"; + case "application/liquid" : return "liquid"; + case "text/x-pascal" : return "pascal"; + case "text/x-python" : return "python"; + case "text/x-rsrc" : return "r"; + case "text/x-ruby" : return "ruby"; + case "text/x-sas" : return "sas"; + case "text/x-sql" : return "sql"; + case "application/typescript" : return "typescript"; + case "text/cql" : return "sql"; // not that bad... + } + if (att.getContentType().contains("json+") || att.getContentType().contains("+json")) { + return "json"; + } + if (att.getContentType().contains("xml+") || att.getContentType().contains("+xml")) { + return "xml"; + } + } + return null; + } + + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java index 9f3ebdbdd..155e8bbc0 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java @@ -106,13 +106,14 @@ public class ProfileDrivenRenderer extends ResourceRenderer { } try { StructureDefinition sd = r.getDefinition(); - ElementDefinition ed = sd.getSnapshot().getElement().get(0); - if (sd.getType().equals("NamingSystem") && "icd10".equals(r.getId())) { - System.out.println("hah!"); + if (sd == null) { + throw new FHIRException("Cannot find definition for "+r.fhirType()); + } else { + ElementDefinition ed = sd.getSnapshot().getElement().get(0); + containedIds.clear(); + hasExtensions = false; + generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), false, 0); } - containedIds.clear(); - hasExtensions = false; - generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), false, 0); } catch (Exception e) { e.printStackTrace(); x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage()); @@ -391,6 +392,8 @@ public class ProfileDrivenRenderer extends ResourceRenderer { } else if (e instanceof DataRequirement) { DataRequirement p = (DataRequirement) e; renderDataRequirement(x, p); + } else if (e instanceof PrimitiveType) { + x.tx(((PrimitiveType) e).primitiveValue()); } else if (e instanceof ElementDefinition) { x.tx("todo-bundle"); } else if (e != null && !(e instanceof Attachment) && !(e instanceof Narrative) && !(e instanceof Meta)) { @@ -485,7 +488,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer { renderAddress(x, (Address) e); return true; } else if (e instanceof ContactPoint) { - renderContactPoint(x, (ContactPoint) e); + if (allowLinks) { + renderContactPoint(x, (ContactPoint) e); + } else { + displayContactPoint(x, (ContactPoint) e); + } return true; } else if (e instanceof Timing) { renderTiming(x, (Timing) e); @@ -527,7 +534,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer { boolean first = true; for (ContactPoint c : cd.getTelecom()) { if (first) first = false; else x.tx(","); - renderContactPoint(x, c); + if (allowLinks) { + renderContactPoint(x, c); + } else { + displayContactPoint(x, c); + } } return true; } else if (e instanceof Range) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java index 5935729b4..fef306984 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java @@ -59,6 +59,9 @@ public class RendererFactory { if ("Encounter".equals(resourceName)) { return new EncounterRenderer(context); } + if ("Library".equals(resourceName)) { + return new LibraryRenderer(context); + } if ("List".equals(resourceName)) { return new ListRenderer(context); } @@ -75,6 +78,9 @@ public class RendererFactory { if ("Parameters".equals(resourceName)) { return new ParametersRenderer(context); } + if ("Bundle".equals(resourceName)) { + return new BundleRenderer(context); + } return new ProfileDrivenRenderer(context); } @@ -102,6 +108,9 @@ public class RendererFactory { if ("List".equals(resource.getName())) { return new ListRenderer(context); } + if ("Library".equals(resource.getName())) { + return new LibraryRenderer(context); + } if ("DiagnosticReport".equals(resource.getName())) { return new DiagnosticReportRenderer(context); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java index 53db3f5ac..deb2b4a38 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java @@ -125,6 +125,44 @@ public abstract class ResourceRenderer extends DataRenderer { } } + public void renderCanonical(Resource res, XhtmlNode x, String url) throws UnsupportedEncodingException, IOException { + ResourceWrapper rw = new ResourceWrapperDirect(this.context, res); + renderCanonical(rw, x, url); + } + + public void renderCanonical(ResourceWrapper rw, XhtmlNode x, String url) throws UnsupportedEncodingException, IOException { + renderCanonical(rw, x, url, true); + } + + public void renderCanonical(ResourceWrapper rw, XhtmlNode x, String url, boolean allowLinks) throws UnsupportedEncodingException, IOException { + if (url == null) { + return; + } + Resource target = context.getWorker().fetchResource(Resource.class, url); + if (target == null || !(target instanceof CanonicalResource)) { + x.code().tx(url); + } else { + CanonicalResource cr = (CanonicalResource) target; + if (url.contains("|")) { + if (target.hasUserData("path")) { + x.ah(target.getUserString("path")).tx(cr.present()+" (version "+cr.getVersion()+")"); + } else { + url = url.substring(0, url.indexOf("|")); + x.code().tx(url); + x.tx(": "+cr.present()+" (version "+cr.getVersion()+")"); + } + } else { + if (target.hasUserData("path")) { + x.ah(target.getUserString("path")).tx(cr.present()); + } else { + url = url.substring(0, url.indexOf("|")); + x.code().tx(url); + x.tx(": "+cr.present()); + } + } + } + } + public void renderReference(Resource res, XhtmlNode x, Reference r) throws UnsupportedEncodingException, IOException { ResourceWrapper rw = new ResourceWrapperDirect(this.context, res); renderReference(rw, x, r); 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..cb0919b01 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); } @@ -827,7 +829,8 @@ public class ValueSetRenderer extends TerminologyRenderer { boolean hasDefinition = false; for (ConceptReferenceComponent c : inc.getConcept()) { hasComments = hasComments || ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT); - hasDefinition = hasDefinition || ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION); + ConceptDefinitionComponent cc = definitions.get(c.getCode()); + hasDefinition = hasDefinition || ((cc != null && cc.hasDefinition()) || ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)); } if (hasComments || hasDefinition) hasExtensions = true; @@ -844,16 +847,19 @@ public class ValueSetRenderer extends TerminologyRenderer { else if (cc != null && !Utilities.noString(cc.getDisplay())) td.addText(cc.getDisplay()); - if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)) { + if (hasDefinition) { td = tr.td(); - smartAddText(td, ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_DEFINITION)); - } else if (cc != null && !Utilities.noString(cc.getDefinition())) { - td = tr.td(); - smartAddText(td, cc.getDefinition()); + if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)) { + smartAddText(td, ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_DEFINITION)); + } else if (cc != null && !Utilities.noString(cc.getDefinition())) { + smartAddText(td, cc.getDefinition()); + } } - - if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT)) { - smartAddText(tr.td(), "Note: "+ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_VS_COMMENT)); + if (hasComments) { + td = tr.td(); + if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT)) { + smartAddText(td, "Note: "+ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_VS_COMMENT)); + } } if (doLangs) { addLangaugesToRow(c, langs, tr); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/BaseWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/BaseWrappers.java index fef5851a2..83101e229 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/BaseWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/BaseWrappers.java @@ -52,6 +52,7 @@ public class BaseWrappers { public void describe(XhtmlNode x) throws UnsupportedEncodingException, IOException; public void injectNarrative(XhtmlNode x, NarrativeStatus status) throws IOException; public BaseWrapper root(); + public PropertyWrapper getChildByName(String tail); public StructureDefinition getDefinition(); public boolean hasNarrative(); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java index 3fd8d6a7c..6fbb5f457 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java @@ -362,6 +362,16 @@ public class DOMWrappers { public String fhirType() { return wrapped.getNodeName(); } + + @Override + public PropertyWrapper getChildByName(String name) { + for (PropertyWrapper p : children()) + if (p.getName().equals(name)) + return p; + return null; + } + + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DirectWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DirectWrappers.java index 7ec2aeb5b..5fe4dc859 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DirectWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DirectWrappers.java @@ -267,6 +267,16 @@ public class DirectWrappers { public String fhirType() { return wrapped.fhirType(); } + + @Override + public PropertyWrapper getChildByName(String name) { + Property p = wrapped.getChildByName(name); + if (p == null) + return null; + else + return new PropertyWrapperDirect(context, p); + } + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ElementWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ElementWrappers.java index 2ca58caf5..c0ab9ff0a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ElementWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ElementWrappers.java @@ -246,7 +246,16 @@ public class ElementWrappers { public String fhirType() { return wrapped.fhirType(); } - } + + @Override + public PropertyWrapper getChildByName(String name) { + for (PropertyWrapper p : children()) + if (p.getName().equals(name)) + return p; + return null; + } + +} public static class PropertyWrapperMetaElement extends RendererWrapperImpl implements PropertyWrapper { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java index 94c413b31..84f0a748d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java @@ -126,7 +126,7 @@ public class Resolver { if (containerElement != null) { for (org.hl7.fhir.r5.elementmodel.Element p : containerElement.getChildren("parameter")) { org.hl7.fhir.r5.elementmodel.Element res = p.getNamedChild("resource"); - if (value.equals(res.fhirType()+"/"+res.getChildValue("id"))) + if (res != null && value.equals(res.fhirType()+"/"+res.getChildValue("id"))) return p; } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java index e8f89e701..cbb70ea89 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java @@ -164,6 +164,9 @@ public class ValueSetCheckerSimple implements ValueSetChecker { throw new FHIRException("Unable to evaluate based on empty code system"); } res = validateCode(code, cs); + } else if (cs == null && valueset.hasExpansion() && inExpansion) { + // we just take the value set as face value then + res = new ValidationResult(IssueSeverity.INFORMATION, null); } else { // well, we didn't find a code system - try the expansion? // disabled waiting for discussion 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..be10c5ba2 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 @@ -1,5 +1,10 @@ package org.hl7.fhir.r5.utils; +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; + +import org.apache.poi.xssf.model.Comments; import org.hl7.fhir.exceptions.FHIRException; /* @@ -34,7 +39,8 @@ 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.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.Utilities; // shared lexer for concrete syntaxes @@ -65,6 +71,7 @@ public class FHIRLexer { private int cursor; private int currentStart; private String current; + private List comments = new ArrayList<>(); private SourceLocation currentLocation; private SourceLocation currentStartLocation; private int id; @@ -146,23 +153,8 @@ public class FHIRLexer { } public void next() throws FHIRLexerException { + skipWhitespaceAndComments(); current = null; - boolean last13 = false; - while (cursor < source.length() && Character.isWhitespace(source.charAt(cursor))) { - if (source.charAt(cursor) == '\r') { - currentLocation.setLine(currentLocation.getLine() + 1); - currentLocation.setColumn(1); - last13 = true; - } else if (!last13 && (source.charAt(cursor) == '\n')) { - currentLocation.setLine(currentLocation.getLine() + 1); - currentLocation.setColumn(1); - last13 = false; - } else { - last13 = false; - currentLocation.setColumn(currentLocation.getColumn() + 1); - } - cursor++; - } currentStart = cursor; currentStartLocation = currentLocation; if (cursor < source.length()) { @@ -208,9 +200,8 @@ public class FHIRLexer { } else if (ch == '/') { cursor++; if (cursor < source.length() && (source.charAt(cursor) == '/')) { - cursor++; - while (cursor < source.length() && !((source.charAt(cursor) == '\r') || source.charAt(cursor) == '\n')) - cursor++; + // this is en error - should already have been skipped + error("This shoudn't happen?"); } current = source.substring(currentStart, cursor); } else if (ch == '$') { @@ -296,7 +287,38 @@ public class FHIRLexer { } } - + private void skipWhitespaceAndComments() { + comments.clear(); + boolean last13 = false; + boolean done = false; + while (cursor < source.length() && !done) { + if (cursor < source.length() -1 && "//".equals(source.substring(cursor, cursor+2))) { + int start = cursor+2; + while (cursor < source.length() && !((source.charAt(cursor) == '\r') || source.charAt(cursor) == '\n')) { + cursor++; + } + comments.add(source.substring(start, cursor).trim()); + } else if (cursor < source.length() - 1 && "/*".equals(source.substring(cursor, cursor+2))) { + int start = cursor+2; + while (cursor < source.length() - 1 && !"*/".equals(source.substring(cursor, cursor+2))) { + last13 = currentLocation.checkChar(source.charAt(cursor), last13); + cursor++; + } + if (cursor >= source.length() -1) { + error("Unfinished comment"); + } else { + comments.add(source.substring(start, cursor).trim()); + cursor = cursor + 2; + } + } else if (Character.isWhitespace(source.charAt(cursor))) { + last13 = currentLocation.checkChar(source.charAt(cursor), last13); + cursor++; + } else { + done = true; + } + } + } + private boolean isDateChar(char ch,int start) { int eot = source.charAt(start+1) == 'T' ? 10 : 20; @@ -321,9 +343,31 @@ public class FHIRLexer { this.current = current; } - public boolean hasComment() { - return !done() && current.startsWith("//"); + public boolean hasComments() { + return comments.size() > 0; } + + public List getComments() { + return comments; + } + + public String getAllComments() { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("\r\n"); + b.addAll(comments); + comments.clear(); + return b.toString(); + } + + public String getFirstComment() { + if (hasComments()) { + String s = comments.get(0); + comments.remove(0); + return s; + } else { + return null; + } + } + public boolean hasToken(String kw) { return !done() && kw.equals(current); } @@ -472,10 +516,6 @@ public class FHIRLexer { return b.toString(); } - void skipComments() throws FHIRLexerException { - while (!done() && hasComment()) - next(); - } public int getCurrentStart() { return currentStart; } 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 e7a7f33a2..8edc8e9d4 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; @@ -105,6 +106,7 @@ import ca.uhn.fhir.util.ElementUtil; * */ public class FHIRPathEngine { + private enum Equality { Null, True, False } private class FHIRConstant extends Base { @@ -211,6 +213,7 @@ public class FHIRPathEngine { private ValidationOptions terminologyServiceOptions = new ValidationOptions(); private ProfileUtilities profileUtilities; private String location; // for error messages + private boolean allowPolymorphicNames; // if the fhir path expressions are allowed to use constants beyond those defined in the specification // the application can implement them by providing a constant resolver @@ -282,7 +285,7 @@ public class FHIRPathEngine { * @param parameters * @return */ - public List executeFunction(Object appContext, String functionName, List> parameters); + public List executeFunction(Object appContext, List focus, String functionName, List> parameters); /** * Implementation of resolve() function. Passed a string, return matching resource, if one is known - else null @@ -372,10 +375,24 @@ public class FHIRPathEngine { * @throws FHIRException */ protected void getChildrenByName(Base item, String name, List result) throws FHIRException { + String tn = null; + if (isAllowPolymorphicNames()) { + // we'll look to see whether we hav a polymorphic name + for (Property p : item.children()) { + if (p.getName().endsWith("[x]")) { + String n = p.getName().substring(0, p.getName().length()-3); + if (name.startsWith(n)) { + tn = name.substring(n.length()); + name = n; + break; + } + } + } + } Base[] list = item.listChildrenByName(name, false); if (list != null) { for (Base v : list) { - if (v != null) { + if (v != null && (tn == null || v.fhirType().equalsIgnoreCase(tn))) { result.add(v); } } @@ -493,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); @@ -514,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 { @@ -528,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); @@ -891,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)); } @@ -940,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); } @@ -951,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)); @@ -1338,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); } @@ -1356,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; @@ -1387,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; } @@ -1397,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; @@ -1428,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; @@ -1447,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)); @@ -1468,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(); } @@ -1477,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; @@ -1539,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); @@ -1555,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")) { @@ -1564,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")) { @@ -1583,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); } @@ -1644,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; @@ -1692,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) @@ -1711,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); @@ -1755,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: @@ -1765,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: @@ -1785,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(); } @@ -1814,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(); } @@ -2045,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); } @@ -2067,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); } @@ -2091,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(); @@ -2117,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); @@ -2132,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()) { @@ -2164,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); @@ -2179,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(); } @@ -2212,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"); @@ -2220,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); @@ -2229,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(); } @@ -2262,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); @@ -2277,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) { @@ -2309,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(); } @@ -2334,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(); } @@ -2356,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(); @@ -2382,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(); @@ -2421,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(); @@ -2451,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)) { @@ -2477,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: @@ -2502,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: @@ -2523,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) { @@ -2545,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); @@ -2560,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(); @@ -2591,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(); @@ -2638,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(); @@ -2679,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(); @@ -2719,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) { @@ -2735,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); @@ -2756,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")) { @@ -2777,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); } @@ -2828,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); } @@ -2840,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; } @@ -2870,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 : @@ -2898,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 : { @@ -2926,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 : { @@ -2974,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 : @@ -3051,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: @@ -3073,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 : @@ -3137,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); } @@ -3166,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()); } @@ -3190,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()) { @@ -3200,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; } @@ -3371,16 +3463,16 @@ public class FHIRPathEngine { for (ExpressionNode p : exp.getParameters()) { params.add(execute(context, focus, p, true)); } - return hostServices.executeFunction(context.appInfo, exp.getName(), params); + return hostServices.executeFunction(context.appInfo, focus, exp.getName(), params); } default: throw new Error("not Implemented yet"); } } - 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(); @@ -3392,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(); @@ -3415,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(); @@ -3434,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(); @@ -3453,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(); @@ -3474,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(); @@ -3494,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()); @@ -3519,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; } @@ -3528,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()); @@ -3547,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(); @@ -3565,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; } @@ -3827,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; @@ -3837,7 +3929,7 @@ public class FHIRPathEngine { } else {// (exp.getParameters().size() == 0) { boolean all = true; for (Base item : focus) { - Equality eq = asBool(item); + Equality eq = asBool(item, true); if (eq != Equality.True) { all = false; break; @@ -3926,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) { @@ -3934,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; } @@ -4033,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)) { @@ -4066,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); @@ -4141,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(); @@ -4198,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.")) { @@ -4350,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; } @@ -4445,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; @@ -4455,7 +4547,11 @@ public class FHIRPathEngine { } else { boolean all = true; for (Base item : focus) { - Equality v = asBool(item); + if (!canConvertToBoolean(item)) { + throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); + } + + Equality v = asBool(item, true); if (v != Equality.False) { all = false; break; @@ -4475,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; @@ -4485,7 +4581,11 @@ public class FHIRPathEngine { } else { boolean any = false; for (Base item : focus) { - Equality v = asBool(item); + if (!canConvertToBoolean(item)) { + throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); + } + + Equality v = asBool(item, true); if (v == Equality.False) { any = true; break; @@ -4505,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; @@ -4515,8 +4615,11 @@ public class FHIRPathEngine { } else { boolean all = true; for (Base item : focus) { - Equality v = asBool(item); - if (v != Equality.True) { + if (!canConvertToBoolean(item)) { + throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); + } + Equality v = asBool(item, true); + if (v != Equality.True) { all = false; break; } @@ -4535,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; @@ -4545,7 +4648,11 @@ public class FHIRPathEngine { } else { boolean any = false; for (Base item : focus) { - Equality v = asBool(item); + if (!canConvertToBoolean(item)) { + throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); + } + + Equality v = asBool(item, true); if (v == Equality.True) { any = true; break; @@ -4556,7 +4663,11 @@ public class FHIRPathEngine { return result; } - private List funcTrace(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { + private boolean canConvertToBoolean(Base item) { + return (item.isBooleanPrimitive()); + } + + private List funcTrace(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List nl = execute(context, focus, exp.getParameters().get(0), true); String name = nl.get(0).primitiveValue(); if (exp.getParameters().size() == 2) { @@ -4568,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; } @@ -4835,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; @@ -5006,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); } @@ -5043,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)); } @@ -5067,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; @@ -5092,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); } @@ -5138,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); } } } @@ -5166,7 +5277,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, isAllowPolymorphicNames(), expr); if (ed != null) { if (!Utilities.noString(ed.getFixedType())) result.addType(ed.getFixedType()); @@ -5224,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()) { @@ -5251,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; @@ -5325,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; } @@ -5338,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()); } @@ -5354,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())) { @@ -5391,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(); @@ -5406,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; @@ -5438,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()); @@ -5474,13 +5585,15 @@ 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()) { + return asBool(items.get(0), true); } else if (items.size() == 1) { - return asBool(items.get(0)); + return Equality.True; } else { - throw makeException(I18nConstants.FHIRPATH_UNABLE_BOOLEAN, convertToString(items)); + throw makeException(expr, I18nConstants.FHIRPATH_UNABLE_BOOLEAN, convertToString(items)); } } @@ -5512,7 +5625,7 @@ public class FHIRPathEngine { } } - private Equality asBool(Base item) { + private Equality asBool(Base item, boolean narrow) { if (item instanceof BooleanType) { return boolToTriState(((BooleanType) item).booleanValue()); } else if (item.isBooleanPrimitive()) { @@ -5523,6 +5636,8 @@ public class FHIRPathEngine { } else { return Equality.Null; } + } else if (narrow) { + return Equality.False; } else if (item instanceof IntegerType || Utilities.existsInList(item.fhirType(), "integer", "positiveint", "unsignedInt")) { return asBoolFromInt(item.primitiveValue()); } else if (item instanceof DecimalType || Utilities.existsInList(item.fhirType(), "decimal")) { @@ -5556,5 +5671,14 @@ public class FHIRPathEngine { public IWorkerContext getWorker() { return worker; } + + public boolean isAllowPolymorphicNames() { + return allowPolymorphicNames; + } + + public void setAllowPolymorphicNames(boolean allowPolymorphicNames) { + this.allowPolymorphicNames = allowPolymorphicNames; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java index 31100c7bf..03dcc8b1f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java @@ -632,11 +632,11 @@ public class LiquidEngine implements IEvaluationContext { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { if (externalHostServices == null) return null; LiquidEngineContext ctxt = (LiquidEngineContext) appContext; - return externalHostServices.executeFunction(ctxt.externalContext, functionName, parameters); + return externalHostServices.executeFunction(ctxt.externalContext, focus, functionName, parameters); } @Override 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..3bbfe0ed3 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; @@ -102,6 +102,7 @@ public class NPMPackageGenerator { private JsonObject packageJ; private JsonObject packageManifest; private NpmPackageIndexBuilder indexer; + private String igVersion; public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date, boolean notForPublication) throws FHIRException, IOException { @@ -184,6 +185,7 @@ public class NPMPackageGenerator { JsonObject npm = new JsonObject(); npm.addProperty("name", ig.getPackageId()); npm.addProperty("version", ig.getVersion()); + igVersion = ig.getVersion(); npm.addProperty("tools-version", ToolsVersion.TOOLS_VERSION); npm.addProperty("type", kind.getCode()); npm.addProperty("date", dt); @@ -395,5 +397,9 @@ public class NPMPackageGenerator { } } + public String version() { + return igVersion; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/StructureMapUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/StructureMapUtilities.java index 83442379c..41040413d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/StructureMapUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/StructureMapUtilities.java @@ -202,7 +202,7 @@ public class StructureMapUtilities { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { throw new Error("Not Implemented Yet"); } @@ -748,15 +748,12 @@ public class StructureMapUtilities { FHIRLexer lexer = new FHIRLexer(text, srcName); if (lexer.done()) throw lexer.error("Map Input cannot be empty"); - lexer.skipComments(); lexer.token("map"); StructureMap result = new StructureMap(); result.setUrl(lexer.readConstant("url")); lexer.token("="); result.setName(lexer.readConstant("name")); - if (lexer.hasComment()) { - result.setDescription(getMultiLineComments(lexer)); - } + result.setDescription(lexer.getAllComments()); while (lexer.hasToken("conceptmap")) parseConceptMap(result, lexer); @@ -782,7 +779,6 @@ public class StructureMapUtilities { map.setStatus(PublicationStatus.DRAFT); // todo: how to add this to the text format result.getContained().add(map); lexer.token("{"); - lexer.skipComments(); // lexer.token("source"); // map.setSource(new UriType(lexer.readConstant("source"))); // lexer.token("target"); @@ -824,25 +820,11 @@ public class StructureMapUtilities { tgt.setCode(lexer.take()); if (tgt.getCode().startsWith("\"")) tgt.setCode(lexer.processConstant(tgt.getCode())); - if (lexer.hasComment()) - tgt.setComment(lexer.take().substring(2).trim()); + tgt.setComment(lexer.getFirstComment()); } lexer.token("}"); } - private String getMultiLineComments(FHIRLexer lexer) { - String comment = null; - while (lexer.hasComment()) { - String newComment = lexer.take().substring(2).trim(); - if (comment == null) { - comment = newComment; - } else { - comment += "\r\n"+newComment; - } - } - return comment; - } - private ConceptMapGroupComponent getGroup(ConceptMap map, String srcs, String tgts) { for (ConceptMapGroupComponent grp : map.getGroup()) { if (grp.getSource().equals(srcs)) @@ -895,9 +877,7 @@ public class StructureMapUtilities { lexer.token("as"); st.setMode(StructureMapModelMode.fromCode(lexer.take())); lexer.skipToken(";"); - if (lexer.hasComment() && currentLine == lexer.getCurrentLocation().getLine()) { - st.setDocumentation(lexer.take().substring(2).trim()); - } + st.setDocumentation(lexer.getFirstComment()); } private void parseImports(StructureMap result, FHIRLexer lexer) throws FHIRException { @@ -905,19 +885,11 @@ public class StructureMapUtilities { int currentLine = lexer.getCurrentLocation().getLine(); result.addImport(lexer.readConstant("url")); lexer.skipToken(";"); - if (lexer.hasComment() && currentLine == lexer.getCurrentLocation().getLine()) { - lexer.next(); - } + lexer.getFirstComment(); } private void parseGroup(StructureMap result, FHIRLexer lexer) throws FHIRException { - String comment = null; - if (lexer.hasComment()) { - comment = getMultiLineComments(lexer); - if (lexer.done()) { - return ; - } - } + String comment = lexer.getAllComments(); lexer.token("group"); StructureMapGroupComponent group = result.addGroup(); if (comment != null) { @@ -975,7 +947,6 @@ public class StructureMapUtilities { parseRule(result, group.getRule(), lexer, true); } } else { - lexer.skipComments(); while (lexer.hasToken("input")) parseInput(group, lexer, false); while (!lexer.hasToken("endgroup")) { @@ -1003,11 +974,8 @@ public class StructureMapUtilities { if (!newFmt) { lexer.token("as"); input.setMode(StructureMapInputMode.fromCode(lexer.take())); - if (lexer.hasComment()) { - input.setDocumentation(lexer.take().substring(2).trim()); - } + input.setDocumentation(lexer.getFirstComment()); lexer.skipToken(";"); - lexer.skipComments(); } } @@ -1018,12 +986,7 @@ public class StructureMapUtilities { lexer.token(":"); lexer.token("for"); } else { - if (lexer.hasComment()) { - rule.setDocumentation(this.getMultiLineComments(lexer)); - if (lexer.hasToken("}")) { - return ; // catched a comment at the end - } - } + rule.setDocumentation(lexer.getFirstComment()); } list.add(rule); boolean done = false; @@ -1062,8 +1025,9 @@ public class StructureMapUtilities { lexer.next(); } } - } else if (lexer.hasComment()) { - rule.setDocumentation(getMultiLineComments(lexer)); + } + if (!rule.hasDocumentation() && lexer.hasComments()) { + rule.setDocumentation(lexer.getFirstComment()); } if (isSimpleSyntax(rule)) { rule.getSourceFirstRep().setVariable(AUTO_VAR_NAME); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java index 40f5f73f6..175fec654 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java @@ -180,6 +180,7 @@ public class ToolingExtensions { public static final String EXT_OLD_CONCEPTMAP_EQUIVALENCE = "http://hl7.org/fhir/1.0/StructureDefinition/extension-ConceptMap.element.target.equivalence"; public static final String EXT_EXP_FRAGMENT = "http://hl7.org/fhir/tools/StructureDefinition/expansion-codesystem-fragment"; public static final String EXT_EXP_TOOCOSTLY = "http://hl7.org/fhir/StructureDefinition/valueset-toocostly"; + public static final String EXT_MUST_SUPPORT = "http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support"; // specific extension helpers @@ -335,6 +336,8 @@ public class ToolingExtensions { return ((DecimalType) ex.getValue()).asStringValue(); if ((ex.getValue() instanceof MarkdownType)) return ((MarkdownType) ex.getValue()).getValue(); + if ((ex.getValue() instanceof PrimitiveType)) + return ((PrimitiveType) ex.getValue()).primitiveValue(); if (!(ex.getValue() instanceof StringType)) return null; return ((StringType) ex.getValue()).getValue(); 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 3aac680b5..e1518e4ef 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 @@ -24,6 +24,7 @@ import org.hl7.fhir.r5.model.Quantity; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.TypeDetails; import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.test.FHIRPathTests.TestResultType; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; @@ -41,6 +42,8 @@ import org.xml.sax.SAXException; public class FHIRPathTests { + public enum TestResultType {OK, SYNTAX, SEMANTICS, EXECUTION} + public class FHIRPathTestEvaluationServices implements IEvaluationContext { @Override @@ -69,7 +72,7 @@ public class FHIRPathTests { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { throw new NotImplementedException("Not done yet (FHIRPathTestEvaluationServices.executeFunction), when item is element"); } @@ -111,6 +114,7 @@ public class FHIRPathTests { XMLUtil.getNamedChildren(dom.getDocumentElement(), "group", groups); for (Element g : groups) { XMLUtil.getNamedChildren(g, "test", list); + XMLUtil.getNamedChildren(g, "modeTest", list); } List objects = new ArrayList<>(); @@ -147,84 +151,118 @@ public class FHIRPathTests { public void test(String name, Element test) throws FileNotFoundException, IOException, FHIRException, org.hl7.fhir.exceptions.FHIRException, UcumException { // Setting timezone for this test. Grahame is in UTC+11, Travis is in GMT, and I'm here in Toronto, Canada with // all my time based tests failing locally... - TimeZone.setDefault(TimeZone.getTimeZone("UTC+1100")); + TimeZone.setDefault(TimeZone.getTimeZone("UTC+1100")); fp.setHostServices(new FHIRPathTestEvaluationServices()); String input = test.getAttribute("inputfile"); String expression = XMLUtil.getNamedChild(test, "expression").getTextContent(); - boolean fail = Utilities.existsInList(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"), "true", "semantic"); + TestResultType fail = TestResultType.OK; + if ("syntax".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"))) { + fail = TestResultType.SYNTAX; + } else if ("semantic".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"))) { + fail = TestResultType.SEMANTICS; + } else if ("execution".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"))) { + fail = TestResultType.EXECUTION; + }; + fp.setAllowPolymorphicNames("lenient/polymorphics".equals(test.getAttribute("mode"))); Resource res = null; List outcome = new ArrayList(); - ExpressionNode node = fp.parse(expression); + System.out.println(name); + + ExpressionNode node = null; try { - if (Utilities.noString(input)) { - fp.check(null, null, node); - } else { - res = resources.get(input); - if (res == null) { - res = new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", input)); - resources.put(input, res); - } - fp.check(res, res.getResourceType().toString(), res.getResourceType().toString(), node); - } - outcome = fp.evaluate(res, node); - Assertions.assertFalse(fail, String.format("Expected exception parsing %s", expression)); + node = fp.parse(expression); + Assertions.assertTrue(fail != TestResultType.SYNTAX, String.format("Expected exception didn't occur parsing %s", expression)); } catch (Exception e) { - Assertions.assertTrue(fail, String.format("Unexpected exception parsing %s: " + e.getMessage(), expression)); + System.out.println("Parsing Error: "+e.getMessage()); + Assertions.assertTrue(fail == TestResultType.SYNTAX, String.format("Unexpected exception parsing %s: " + e.getMessage(), expression)); + } + + if (node != null) { + try { + if (Utilities.noString(input)) { + fp.check(null, null, node); + } else { + res = resources.get(input); + if (res == null) { + res = new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", input)); + resources.put(input, res); + } + fp.check(res, res.getResourceType().toString(), res.getResourceType().toString(), node); + } + 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; + } + } + + if (node != null) { + try { + 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; + } } - if ("true".equals(test.getAttribute("predicate"))) { - boolean ok = fp.convertToBoolean(outcome); - outcome.clear(); - outcome.add(new BooleanType(ok)); - } - System.out.println(name); if (fp.hasLog()) { System.out.println(name); System.out.println(fp.takeLog()); } - List expected = new ArrayList(); - XMLUtil.getNamedChildren(test, "output", expected); - Assertions.assertEquals(outcome.size(), expected.size(), String.format("Expected %d objects but found %d for expression %s", expected.size(), outcome.size(), expression)); - if ("false".equals(test.getAttribute("ordered"))) { - for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) { - String tn = outcome.get(i).fhirType(); - String s; - if (outcome.get(i) instanceof Quantity) { - s = fp.convertToString(outcome.get(i)); - } else { - s = ((PrimitiveType) outcome.get(i)).asStringValue(); - } - boolean found = false; - for (Element e : expected) { - if ((Utilities.noString(e.getAttribute("type")) || e.getAttribute("type").equals(tn)) && - (Utilities.noString(e.getTextContent()) || e.getTextContent().equals(s))) { - found = true; - } - } - Assertions.assertTrue(found, String.format("Outcome %d: Value %s of type %s not expected for %s", i, s, tn, expression)); + if (node != null) { + if ("true".equals(test.getAttribute("predicate"))) { + boolean ok = fp.convertToBoolean(outcome); + outcome.clear(); + outcome.add(new BooleanType(ok)); } - } else { - for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) { - String tn = expected.get(i).getAttribute("type"); - if (!Utilities.noString(tn)) { - Assertions.assertEquals(tn, outcome.get(i).fhirType(), String.format("Outcome %d: Type should be %s but was %s", i, tn, outcome.get(i).fhirType())); - } - String v = expected.get(i).getTextContent(); - if (!Utilities.noString(v)) { + + List expected = new ArrayList(); + XMLUtil.getNamedChildren(test, "output", expected); + Assertions.assertEquals(outcome.size(), expected.size(), String.format("Expected %d objects but found %d for expression %s", expected.size(), outcome.size(), expression)); + if ("false".equals(test.getAttribute("ordered"))) { + for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) { + String tn = outcome.get(i).fhirType(); + String s; if (outcome.get(i) instanceof Quantity) { - Quantity q = fp.parseQuantityString(v); - Assertions.assertTrue(outcome.get(i).equalsDeep(q), String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString())); + s = fp.convertToString(outcome.get(i)); } 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()))) { - 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)); + s = ((PrimitiveType) outcome.get(i)).asStringValue(); + } + boolean found = false; + for (Element e : expected) { + if ((Utilities.noString(e.getAttribute("type")) || e.getAttribute("type").equals(tn)) && + (Utilities.noString(e.getTextContent()) || e.getTextContent().equals(s))) { + found = true; + } + } + Assertions.assertTrue(found, String.format("Outcome %d: Value %s of type %s not expected for %s", i, s, tn, expression)); + } + } else { + for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) { + String tn = expected.get(i).getAttribute("type"); + if (!Utilities.noString(tn)) { + Assertions.assertEquals(tn, outcome.get(i).fhirType(), String.format("Outcome %d: Type should be %s but was %s", i, tn, outcome.get(i).fhirType())); + } + String v = expected.get(i).getTextContent(); + if (!Utilities.noString(v)) { + if (outcome.get(i) instanceof Quantity) { + Quantity q = fp.parseQuantityString(v); + 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)).fpValue()))) { + System.out.println(name); + 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)).fpValue(), 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)); } } } 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..135934e47 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; @@ -380,7 +380,7 @@ public class SnapShotGenerationTests { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { if ("fixture".equals(functionName)) { String id = fp.convertToString(parameters.get(0)); Resource res = fetchFixture(id); 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.report/pom.xml b/org.hl7.fhir.report/pom.xml index 721217440..ef4bb14a3 100644 --- a/org.hl7.fhir.report/pom.xml +++ b/org.hl7.fhir.report/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index 07d898511..cbe28db37 100644 --- a/org.hl7.fhir.utilities/pom.xml +++ b/org.hl7.fhir.utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml 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/CommaSeparatedStringBuilder.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/CommaSeparatedStringBuilder.java index 3973f1506..89c2eec92 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/CommaSeparatedStringBuilder.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/CommaSeparatedStringBuilder.java @@ -1,5 +1,7 @@ package org.hl7.fhir.utilities; +import java.util.List; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -76,4 +78,11 @@ public class CommaSeparatedStringBuilder { append(s); } + + public void addAll(List list) { + for (String s : list) { + append(s); + } + + } } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/MimeType.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/MimeType.java index e3309e12c..d3a3f0b8d 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/MimeType.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/MimeType.java @@ -90,4 +90,49 @@ public class MimeType { return source; } + public static String getExtension(String mimeType) { + MimeType mt = new MimeType(mimeType); + return mt.getExtension(); + } + + public String getExtension() { + switch (base) { + case "text/html" : return "html"; + case "text/xml" : return "xml"; + case "application/xml" : return "xml"; + case "text/markdown" : return "md"; + case "application/js" : return "js"; + case "application/css" : return "css"; + case "text/x-csrc" : return "c"; + case "text/x-csharp" : return "cs"; + case "text/x-c++src" : return "c"; + case "application/graphql" : return "graphql"; + case "application/x-java" : return "java"; + case "application/json" : return "json"; + case "text/json" : return "json"; + case "application/liquid" : return "liquid"; + case "text/x-pascal" : return "pas"; + case "text/x-python" : return "py"; + case "text/x-rsrc" : return "r"; + case "text/x-ruby" : return "ruby"; + case "text/x-sas" : return "sas"; + case "text/x-sql" : return "sql"; + case "application/typescript" : return "ts"; + case "text/cql": return "cql"; + case "image/png": return "png"; + case "image/gif": return "gif"; + case "image/jpeg": return "jpg"; + } + if (base.contains("xml+") || base.contains("+xml")) { + return "xml"; + } + if (base.contains("json+") || base.contains("+json")) { + return "json"; + } + if (base.contains("turtle+") || base.contains("+turtle")) { + return "ttl"; + } + return null; + } + } \ 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..93c15dacb --- /dev/null +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SourceLocation.java @@ -0,0 +1,46 @@ +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); + } + + public void newLine() { + setLine(getLine() + 1); + setColumn(1); + } + public boolean checkChar(char ch, boolean last13) { + if (ch == '\r') { + newLine(); + return true; + } else if (ch == '\n') { + if (!last13) { + newLine(); + } + return false; + } else { + setColumn(getColumn() + 1); + return false; + } + } +} diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index 2015c3e22..82d06017a 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -47,6 +47,8 @@ import java.math.RoundingMode; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -320,13 +322,13 @@ public class Utilities { destFile.createNewFile(); } - FileChannel source = null; - FileChannel destination = null; + FileInputStream source = null; + FileOutputStream destination = null; try { - source = new FileInputStream(sourceFile).getChannel(); - destination = new FileOutputStream(destFile).getChannel(); - destination.transferFrom(source, 0, source.size()); + source = new FileInputStream(sourceFile); + destination = new FileOutputStream(destFile); + destination.getChannel().transferFrom(source.getChannel(), 0, source.getChannel().size()); } finally { if (source != null) { source.close(); @@ -398,8 +400,8 @@ public class Utilities { clearDirectory(fh.getAbsolutePath()); fh.delete(); } + } } - } } } } 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 3b303a035..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. @@ -181,6 +181,10 @@ public class VersionUtilities { public static String getMajMin(String version) { if (version == null) return null; + + if ("current".equals(version)) { + return CURRENT_VERSION; + } if (Utilities.charCount(version, '.') == 1) { String[] p = version.split("\\."); @@ -193,6 +197,16 @@ public class VersionUtilities { } } + public static String getPatch(String version) { + if (version == null) + return null; + if (Utilities.charCount(version, '.') == 2) { + String[] p = version.split("\\."); + return p[2]; + } + return null; + } + public static boolean isSemVer(String version) { if (Utilities.charCount(version, '.') != 2) { return false; @@ -202,7 +216,7 @@ public class VersionUtilities { } /** - * return true if the current vresion equals test, or later + * return true if the current version equals test, or later * * so if a feature is defined in 4.0, if (VersionUtilities.isThisOrLater("4.0", version))... * @@ -213,10 +227,36 @@ public class VersionUtilities { public static boolean isThisOrLater(String test, String current) { String t = getMajMin(test); String c = getMajMin(current); + if (c.compareTo(t) == 0) { + return isMajMinOrLaterPatch(test, current); + } boolean ok = c.compareTo(t) >= 0; return ok; } + /** + * return true if the current version equals test for major and min, or later patch + * + * @param test + * @param current + * @return + */ + public static boolean isMajMinOrLaterPatch(String test, String current) { + String t = getMajMin(test); + String c = getMajMin(current); + if (c != null && c.compareTo(t) == 0) { + String pt = getPatch(test); + String pc = getPatch(current); + if (pt==null || "x".equals(pt)) { + return true; + } + if (pc!=null) { + return pc.compareTo(pt) >= 0; + } + } + return false; + } + public static String incMajorVersion(String v) { assert isSemVer(v); int[] parts = splitParts(v); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 1d5359d76..4d9da697d 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -341,6 +341,8 @@ public class I18nConstants { public static final String RESOURCE_TYPE_MISMATCH_FOR___ = "Resource_type_mismatch_for___"; public static final String SAME_ID_ON_MULTIPLE_ELEMENTS__IN_ = "Same_id_on_multiple_elements__in_"; public static final String SD_MUST_HAVE_DERIVATION = "SD_MUST_HAVE_DERIVATION"; + public static final String SD_NESTED_MUST_SUPPORT_DIFF = "SD_NESTED_MUST_SUPPORT_DIFF"; + public static final String SD_NESTED_MUST_SUPPORT_SNAPSHOT = "SD_NESTED_MUST_SUPPORT_SNAPSHOT"; public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG"; public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG"; public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND"; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java index e133d2f02..977d6873b 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java @@ -36,6 +36,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; +import java.net.HttpURLConnection; +import java.net.URL; import java.util.Map; import java.util.Stack; @@ -671,6 +673,13 @@ public class JsonTrackingParser { Gson gson = new GsonBuilder().setPrettyPrinting().create(); return gson.toJson(json); } + + public static JsonObject fetchJson(String source) throws IOException { + URL url = new URL(source+"?nocache=" + System.currentTimeMillis()); + HttpURLConnection c = (HttpURLConnection) url.openConnection(); + c.setInstanceFollowRedirects(true); + return parseJson(c.getInputStream()); + } } \ No newline at end of file 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 97% 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 82e295e89..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; @@ -69,6 +69,9 @@ public abstract class BasePackageCacheManager implements IPackageCacheManager { if (Utilities.noString(version)) { version = packageClient.getLatestVersion(id); } + if (version.endsWith(".x")) { + version = packageClient.getLatestVersion(id, version); + } InputStream stream = packageClient.fetch(id, version); String url = packageClient.url(id, version); return new InputStreamWithSrc(stream, url, version); 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 95% 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 bf9bdac65..985065479 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. @@ -39,8 +39,9 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.utilities.IniFile; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.cache.NpmPackage.NpmPackageFolder; +import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.json.JSONUtil; +import org.hl7.fhir.utilities.npm.NpmPackage.NpmPackageFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -283,7 +284,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple } /** - * Load the identified package from the cache - it it exists + * Load the identified package from the cache - if it exists *

* This is for special purpose only (testing, control over speed of loading). * Generally, use the loadPackage method @@ -307,11 +308,26 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple return p; } } + String foundPackage = null; + String foundVersion = null; for (String f : sorted(new File(cacheFolder).list())) { - if (f.equals(id + "#" + version) || (Utilities.noString(version) && f.startsWith(id + "#"))) { - return loadPackageInfo(Utilities.path(cacheFolder, f)); + File cf = new File(Utilities.path(cacheFolder, f)); + if (cf.isDirectory()) { + if (f.equals(id + "#" + version) || (Utilities.noString(version) && f.startsWith(id + "#"))) { + return loadPackageInfo(Utilities.path(cacheFolder, f)); + } + if (version != null && version.endsWith(".x") && f.contains("#")) { + String[] parts = f.split("#"); + if (parts[0].equals(id) && VersionUtilities.isMajMinOrLaterPatch((foundVersion!=null ? foundVersion : version),parts[1])) { + foundVersion = parts[1]; + foundPackage = f; + } + } } } + if (foundPackage!=null) { + return loadPackageInfo(Utilities.path(cacheFolder, foundPackage)); + } if ("dev".equals(version)) return loadPackageFromCacheOnly(id, "current"); else @@ -895,33 +911,4 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple return false; } - - - -//public List getUrls() throws IOException { -// if (allUrls == null) -// { -// IniFile ini = new IniFile(Utilities.path(cacheFolder, "packages.ini")); -// allUrls = new ArrayList<>(); -// for (String s : ini.getPropertyNames("urls")) -// allUrls.add(ini.getStringProperty("urls", s)); -// try { -// URL url = new URL("https://raw.githubusercontent.com/FHIR/ig-registry/master/fhir-ig-list.json?nocache=" + System.currentTimeMillis()); -// HttpURLConnection connection = (HttpURLConnection) url.openConnection(); -// connection.setRequestMethod("GET"); -// InputStream json = connection.getInputStream(); -// JsonObject packages = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.streamToString(json)); -// JsonArray guides = packages.getAsJsonArray("guides"); -// for (JsonElement g : guides) { -// JsonObject gi = (JsonObject) g; -// if (gi.has("canonical")) -// if (!allUrls.contains(gi.get("canonical").getAsString())) -// allUrls.add(gi.get("canonical").getAsString()); -// } -// } catch (Exception e) { -// System.out.println("Listing known Implementation Guides failed: "+e.getMessage()); -// } -// } -// return allUrls; -//} } \ No newline at end of file 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 b87b60818..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; @@ -814,7 +814,7 @@ public class NpmPackage { public String getWebLocation() { if (npm.has("url")) { - return npm.get("url").getAsString(); + return PackageHacker.fixPackageUrl(npm.get("url").getAsString()); } else { return JSONUtil.str(npm, "canonical"); } 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 73% 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 ac1457b08..3fdaa7e35 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; @@ -8,6 +8,7 @@ import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.json.JSONUtil; +import org.hl7.fhir.utilities.json.JsonTrackingParser; import java.io.File; import java.io.FileInputStream; @@ -208,5 +209,61 @@ public class PackageClient { } } + public String getLatestVersion(String id, String majMinVersion) throws IOException { + List list = getVersions(id); + if (list.isEmpty()) { + throw new IOException("Package not found: "+id); + } else { + String v = majMinVersion; + for (PackageInfo p : list) { + if (VersionUtilities.isMajMinOrLaterPatch(v, p.version)) { + v = p.version; + } + } + return v; + } + } + + public List listFromRegistry(String name, String canonical, String fhirVersion) throws IOException { + List result = new ArrayList<>(); + JsonObject packages = JsonTrackingParser.fetchJson("https://raw.githubusercontent.com/FHIR/ig-registry/master/fhir-ig-list.json?nocache=" + System.currentTimeMillis()); + for (JsonObject o : JSONUtil.objects(packages, "guides")) { + if (o.has("canonical")) { + String id = JSONUtil.str(o, "npm-name"); + String pname = JSONUtil.str(o, "name"); + String pcanonical = JSONUtil.str(o, "canonical"); + String description = JSONUtil.str(o, "description"); + boolean ok = true; + if (ok && !Utilities.noString(name)) { + ok = (pname != null && pname.contains(name)) || (description != null && description.contains(name)) || (id != null && id.contains(name)); + } + if (ok && !Utilities.noString(canonical)) { + ok = pcanonical.contains(canonical); + } + String version = null; + String fVersion = null; + String url = null; + + if (ok) { + // if we can find something... + for (JsonObject e : JSONUtil.objects(o, "editions")) { + if (fhirVersion == null || fhirVersion.equals(JSONUtil.str(e, "fhir-version"))) { + String v = JSONUtil.str(e, "ig-version"); + if (version == null || VersionUtilities.isThisOrLater(version, v)) { + version = v; + fVersion = e.getAsJsonArray("fhir-version").get(0).getAsString(); + url = JSONUtil.str(e, "url"); + } + } + } + } + if (version != null) { + result.add(new PackageInfo(id, version, fVersion, description, url, pcanonical)); + } + } + } + return result; + } + } \ No newline at end of file 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 92% 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 ba3319bbe..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; @@ -25,7 +25,7 @@ import com.google.gson.JsonObject; public class PackageHacker { public static void main(String[] args) throws FileNotFoundException, IOException { - new PackageHacker().edit("M:\\web\\hl7.org\\fhir\\uv\\cdisc-lab\\package.tgz"); + new PackageHacker().edit("M:\\web\\hl7.org\\fhir\\us\\carin-rtpbc\\package.tgz"); } private void edit(String name) throws FileNotFoundException, IOException { @@ -58,9 +58,9 @@ public class PackageHacker { private void change(JsonObject npm, Map content) throws FileNotFoundException, IOException { fixVersions(npm); npm.remove("url"); - npm.addProperty("url", "http://hl7.org/fhir/uv/cdisc-lab/STU1"); - npm.remove("name"); - npm.addProperty("name", "hl7.fhir.uv.cdisc-lab"); + npm.addProperty("url", "http://hl7.org/fhir/us/carin-rtpbc/STU1"); +// npm.remove("name"); +// npm.addProperty("name", "hl7.fhir.uv.smart-app-launch"); // npm.remove("canonical"); // npm.addProperty("canonical", "http://hl7.org/fhir/us/davinci-drug-formulary"); //// npm.remove("description"); @@ -70,7 +70,7 @@ public class PackageHacker { // npm.remove("dependencies"); // JsonObject dep = new JsonObject(); // npm.add("dependencies", dep); -// dep.addProperty("hl7.fhir.r4.core", "4.0.1"); +// dep.addProperty("hl7.fhir.r3.core", "3.0.1"); // dep.addProperty("hl7.fhir.r4.examples", "4.0.1"); // dep.addProperty("hl7.fhir.r4.expansions", "4.0.1"); // dep.addProperty("hl7.fhir.r4.elements", "4.0.1"); @@ -80,7 +80,7 @@ public class PackageHacker { npm.remove("fhirVersions"); JsonArray a = new JsonArray(); npm.add("fhirVersions", a); - a.add("4.5.0"); + a.add("3.0.1"); } private void setProperty(JsonObject npm, String name, String value) { 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/java/org/hl7/fhir/utilities/turtle/Turtle.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/turtle/Turtle.java index a92f03c66..a555698a3 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/turtle/Turtle.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/turtle/Turtle.java @@ -384,12 +384,12 @@ public class Turtle { public String asHtml() throws Exception { StringBuilder b = new StringBuilder(); - b.append("

\r\n");
+    b.append("
\r\n");
     commitPrefixes(b);
     for (Section s : sections) {
       commitSection(b, s);
     }
-    b.append("
\r\n"); + b.append("
\r\n"); b.append("\r\n"); return b.toString(); } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java index d501ff789..e561009f2 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java @@ -38,9 +38,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.instance.model.api.IBaseXhtml; +import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; @@ -86,6 +90,9 @@ public class XhtmlNode implements IBaseXhtml { private List childNodes = new ArrayList(); private String content; private boolean notPretty; + private boolean inPara; + private boolean inLink; + public XhtmlNode() { super(); @@ -140,7 +147,7 @@ public class XhtmlNode implements IBaseXhtml { return this; } - public void validate(List errors, String path, boolean inResource, boolean inPara) { + public void validate(List errors, String path, boolean inResource, boolean inPara, boolean inLink) { if (nodeType == NodeType.Element || nodeType == NodeType.Document) { path = Utilities.noString(path) ? name : path+"/"+name; if (inResource) { @@ -168,13 +175,19 @@ public class XhtmlNode implements IBaseXhtml { if (inPara && Utilities.existsInList(name, "div", "blockquote", "table", "ol", "ul", "p")) { errors.add("Error at "+path+": Found "+name+" inside an html paragraph"); } + if (inLink && Utilities.existsInList(name, "a")) { + errors.add("Error at "+path+": Found an inside an paragraph"); + } if (childNodes != null) { if ("p".equals(name)) { inPara = true; } + if ("a".equals(name)) { + inLink = true; + } for (XhtmlNode child : childNodes) { - child.validate(errors, path, inResource, inPara); + child.validate(errors, path, inResource, inPara, inLink); } } } @@ -187,8 +200,20 @@ public class XhtmlNode implements IBaseXhtml { throw new Error("Wrong node type - node is "+nodeType.toString()+" ('"+getName()+"/"+getContent()+"')"); } +// if (inPara && name.equals("p")) { +// throw new FHIRException("nested Para"); +// } +// if (inLink && name.equals("a")) { +// throw new FHIRException("Nested Link"); +// } XhtmlNode node = new XhtmlNode(NodeType.Element); node.setName(name); + if (inPara || name.equals("p")) { + node.inPara = true; + } + if (inLink || name.equals("a")) { + node.inLink = true; + } childNodes.add(node); return node; } @@ -199,6 +224,12 @@ public class XhtmlNode implements IBaseXhtml { if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) throw new Error("Wrong node type. is "+nodeType.toString()); XhtmlNode node = new XhtmlNode(NodeType.Element); + if (inPara || name.equals("p")) { + node.inPara = true; + } + if (inLink || name.equals("a")) { + node.inLink = true; + } node.setName(name); childNodes.add(index, node); return node; @@ -796,6 +827,21 @@ public class XhtmlNode implements IBaseXhtml { } + public void markdown(String md, String source) throws IOException { + if (md != null) { + String s = new MarkDownProcessor(Dialect.COMMON_MARK).process(md, source); + XhtmlParser p = new XhtmlParser(); + XhtmlNode m; + try { + m = p.parse("
"+s+"
", "div"); + } catch (org.hl7.fhir.exceptions.FHIRFormatError e) { + throw new FHIRFormatError(e.getMessage(), e); + } + getChildNodes().addAll(m.getChildNodes()); + } + } + + diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 8a13d0256..f9d145fbf 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -10,8 +10,8 @@ Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type ''{0}'' Bundle_BUNDLE_Entry_NotFound = Can''t find ''{0}'' in the bundle ({1}) Bundle_BUNDLE_Entry_Orphan = Entry {0} isn''t reachable by traversing from first Bundle entry -Bundle_BUNDLE_Entry_Type = The type ''{0}'' is not valid - no resources allowed here -Bundle_BUNDLE_Entry_Type2 = The type ''{0}'' is not valid - must be {1} +Bundle_BUNDLE_Entry_Type = The type ''{0}'' is not valid - no resources allowed here (allowed = {1}) +Bundle_BUNDLE_Entry_Type2 = The type ''{0}'' is not valid - must be {1} (allowed = {2}) Bundle_BUNDLE_Entry_Type3 = The type ''{0}'' is not valid - must be one of {1} Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl Bundle_BUNDLE_FullUrl_NeedVersion = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references @@ -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,17 +148,17 @@ 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_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_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 (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}) Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) @@ -608,4 +608,7 @@ REFERENCE_REF_SUSPICIOUS = The syntax of the reference ''{0}'' looks incorrect, TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS = UCUM Codes that contain human readable annotations like {0} can be misleading. Best Practice is not to use annotations in the UCUM code, and rather to make sure that Quantity.unit is correctly human readable XHTML_XHTML_ELEMENT_ILLEGAL_IN_PARA = Illegal element name inside in a paragraph in the XHTML (''{0}'') UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported property {3} on type {2} for pattern for discriminator({0}) for slice {1} -UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported: no properties with values found on type {2} for pattern for discriminator({0}) for slice {1} \ No newline at end of file +UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported: no properties with values found on type {2} for pattern for discriminator({0}) for slice {1} +SD_NESTED_MUST_SUPPORT_DIFF = The element {0} has types/profiles/targets that are marked as must support, but the element itself is not marked as must-support. The inner must-supports will be ignored unless the element inherits must-support = true +SD_NESTED_MUST_SUPPORT_SNAPSHOT = The element {0} has types/profiles/targets that are marked as must support, but the element itself is not marked as must-support + \ No newline at end of file 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..3ba3e61e3 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; @@ -104,4 +104,12 @@ public class CachingPackageClientTests { List matches = client.getVersions("Simplifier.Core.STU3X"); Assertions.assertTrue(matches.size() == 0); } + + @Test + public void testRegistry() throws IOException { + CachingPackageClient client = new CachingPackageClient("http://packages.fhir.org/packages"); + List matches1 = client.listFromRegistry(null, null, null); + List matches2 = client.listFromRegistry(null, null, "4.0.1"); + Assertions.assertTrue(matches1.size() > matches2.size()); + } } \ No newline at end of file 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 4f42035eb..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; @@ -37,4 +37,14 @@ public class PackageCacheTests { list = cache.listPackages(); Assertions.assertFalse(list.isEmpty()); } + + @Test + public void testPatchWildCard() throws IOException { + FilesystemPackageCacheManager cache = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); + cache.clear(); + Assertions.assertEquals(cache.loadPackage("hl7.fhir.us.core", "3.1.0").version(), "3.1.0"); + Assertions.assertEquals(cache.loadPackage("hl7.fhir.us.core", "3.1.1").version(), "3.1.1"); + Assertions.assertEquals(cache.loadPackage("hl7.fhir.us.core", "3.1.x").version(), "3.1.1"); + Assertions.assertEquals(cache.loadPackage("hl7.fhir.us.core", "3.0.x").version(), "3.0.1"); + } } \ No newline at end of file diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index 07c20fc2f..a8dcf2676 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index db2bc5c32..ffa5971b1 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.1.11-SNAPSHOT + 5.1.17-SNAPSHOT ../pom.xml 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 9e1bd5dd3..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 @@ -296,9 +296,10 @@ public class BaseValidator { * 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 hint(List errors, IssueType type, String path, boolean thePass, String msg) { + protected boolean hint(List errors, IssueType type, String path, boolean thePass, String theMessage, Object... theMessageArguments) { if (!thePass) { - addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.INFORMATION, null); + String message = context.formatMessage(theMessage, theMessageArguments); + addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.INFORMATION, null); } return thePass; } @@ -528,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 b6ca6b9f2..596055d66 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 @@ -762,6 +796,18 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst context.getLoadedPackages().add(pi.name()+"#"+pi.version()); Map res = new HashMap(); for (String s : pi.dependencies()) { + if (s.endsWith(".x") && s.length()>2) { + String packageMajorMinor = s.substring(0, s.length()-2); + boolean found = false; + for (int i=0; i questionnaires) { + public void setQuestionnaireMode(Validator.QuestionnaireMode questionnaireMode) { + this.questionnaireMode = questionnaireMode; } public void setNative(boolean doNative) { @@ -1123,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); @@ -1180,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 { @@ -1252,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); @@ -1281,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); @@ -1473,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); } @@ -1518,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); @@ -1526,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)) @@ -1583,11 +1630,14 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst validator.getImplementationGuides().addAll(igs); validator.getBundleValidationRules().addAll(bundleValidationRules); validator.getValidationControl().putAll(validationControl ); + validator.setQuestionnaireMode(questionnaireMode); return validator; } public void setMapLog(String mapLog) throws FileNotFoundException { - this.mapLog = new PrintWriter(mapLog); + if (mapLog != null) { + this.mapLog = new PrintWriter(mapLog); + } } public void prepare() { @@ -2042,7 +2092,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 5f641c3c9..e42434539 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; @@ -225,7 +226,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { throw new Error(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESEXECUTEFUNCTION)); } @@ -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); @@ -483,7 +485,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private boolean isKnownExtension(String url) { // Added structuredefinition-expression and following extensions explicitly because they weren't defined in the version of the spec they need to be used with - if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression") || url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION)) + if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression")) return true; for (String s : extensionDomains) if (url.startsWith(s)) @@ -1521,6 +1523,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (extensionUrl.equals(profile.getUrl())) { rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url), I18nConstants.EXTENSION_EXT_SUBEXTENSION_INVALID, url, profile.getUrl()); } + } else if (SpecialExtensions.isKnownExtension(url)) { + ex = SpecialExtensions.getDefinition(url); } else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN_NOTHERE, url)) { hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN, url); } @@ -1556,6 +1560,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return ex; } + private boolean hasExtensionSlice(StructureDefinition profile, String sliceName) { for (ElementDefinition ed : profile.getSnapshot().getElement()) { if (ed.getPath().equals("Extension.extension.url") && ed.hasFixed() && sliceName.equals(ed.getFixed().primitiveValue())) { @@ -1933,7 +1938,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (fetcher != null) { boolean found; try { - found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url); + found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url) || + SpecialExtensions.isKnownExtension(url); } catch (IOException e1) { found = false; } @@ -3840,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")) { @@ -3943,15 +3949,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void validateContains(ValidatorHostContext hostContext, List errors, String path, ElementDefinition child, ElementDefinition context, Element resource, Element element, NodeStack stack, IdStatus idstatus) throws FHIRException { String resourceName = element.getType(); TypeRefComponent trr = null; + CommaSeparatedStringBuilder bt = new CommaSeparatedStringBuilder(); for (TypeRefComponent tr : child.getType()) { - if (tr.getCode().equals("Resource")) { + bt.append(tr.getCode()); + if (tr.getCode().equals("Resource") || tr.getCode().equals(resourceName) ) { trr = tr; break; } } stack.qualifyPath(".ofType("+resourceName+")"); if (trr == null) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE, resourceName); + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE, resourceName, bt.toString()); } else if (isValidResourceType(resourceName, trr)) { // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise ValidatorHostContext hc = null; @@ -4002,7 +4010,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } private boolean isValidResourceType(String type, TypeRefComponent def) { - if (!def.hasProfile()) { + if (!def.hasProfile() && def.getCode().equals("Resource")) { + return true; + } + if (def.getCode().equals(type)) { return true; } List list = new ArrayList<>(); @@ -5072,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/SpecialExtensions.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/SpecialExtensions.java new file mode 100644 index 000000000..a51f75ef1 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/SpecialExtensions.java @@ -0,0 +1,39 @@ +package org.hl7.fhir.validation.instance; + +import java.io.IOException; +import java.io.InputStream; + +import org.hl7.fhir.convertors.VersionConvertorConstants; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.formats.XmlParser; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.utilities.Utilities; + +public class SpecialExtensions { + + // copied from R5 spec + private static final String MUST_SUPPORT_SOURCE = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"elementdefinition-type-must-support\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"fhir\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support\",\"version\" : \"4.5.0\",\"name\" : \"type-must-support\",\"status\" : \"draft\",\"date\" : \"2015-02-28\",\"publisher\" : \"Health Level Seven, Inc. - FHIR Core WG\",\"contact\" : [{\"telecom\" : [{\"system\" : \"url\",\"value\" : \"http://hl7.org/special/committees/FHIR\"}]}],\"description\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"fhirVersion\" : \"4.5.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"ElementDefinition.type\"},{\"type\" : \"element\",\"expression\" : \"ElementDefinition.type.profile\"},{\"type\" : \"element\",\"expression\" : \"ElementDefinition.type.targetProfile\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"An element may be labelled as must support. This extension clarifies which types/profiles/targetProfiles are must-support. It has no meaning if the element itself is not must-support. If the element is labelled must-support, and none of the options are labelled as must support, then an application must support at least one of the possible options, but is not required to support all of them.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"string\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"boolean\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"An element may be labelled as must support. This extension clarifies which types/profiles/targetProfiles are must-support. It has no meaning if the element itself is not must-support. If the element is labelled must-support, and none of the options are labelled as must support, then an application must support at least one of the possible options, but is not required to support all of them.\",\"min\" : 0,\"max\" : \"1\"},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"boolean\"}]}]}}"; + + public static boolean isKnownExtension(String url) { + return Utilities.existsInList(url, + "http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support" + ); + } + + public static StructureDefinition getDefinition(String url) { + try { + switch (url) { + case "http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support" : return makeMustSupport(); + default: return null; + } + } catch (Exception e) { + return null; + } + } + + private static StructureDefinition makeMustSupport() throws FHIRFormatError, IOException { + return (StructureDefinition) new JsonParser().parse(MUST_SUPPORT_SOURCE); + } + +} 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/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index d7dec41cd..f7631ef8b 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -28,6 +28,7 @@ import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.SearchParameter; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.utils.FHIRPathEngine; +import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; @@ -95,6 +96,69 @@ public class StructureDefinitionValidator extends BaseValidator { } catch (FHIRException | IOException e) { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage()); } + List differentials = src.getChildrenByName("differential"); + List snapshots = src.getChildrenByName("snapshot"); + for (Element differential : differentials) { + validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0); + } + for (Element snapshot : snapshots) { + validateElementList(errors, snapshot, stack.push(snapshot, -1, null, null), true, true); + } + } + + private void validateElementList(List errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot) { + List elements = elementList.getChildrenByName("element"); + int cc = 0; + for (Element element : elements) { + validateElementDefinition(errors, element, stack.push(element, cc, null, null), snapshot, hasSnapshot); + cc++; + } + } + + private void validateElementDefinition(List errors, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot) { + boolean typeMustSupport = false; + List types = element.getChildrenByName("type"); + for (Element type : types) { + if (hasMustSupportExtension(type)) { + typeMustSupport = true; + } + } + if (typeMustSupport) { + if (snapshot) { + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_SNAPSHOT, element.getNamedChildValue("path")); + } else { + hint(errors, IssueType.EXCEPTION, stack.getLiteralPath(), hasSnapshot || "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_DIFF, element.getNamedChildValue("path")); + } + } + } + + private boolean hasMustSupportExtension(Element type) { + if ("true".equals(getExtensionValue(type, ToolingExtensions.EXT_MUST_SUPPORT))) { + return true; + } + List profiles = type.getChildrenByName("profile"); + for (Element profile : profiles) { + if ("true".equals(getExtensionValue(profile, ToolingExtensions.EXT_MUST_SUPPORT))) { + return true; + } + } + profiles = type.getChildrenByName("targetProfile"); + for (Element profile : profiles) { + if ("true".equals(getExtensionValue(profile, ToolingExtensions.EXT_MUST_SUPPORT))) { + return true; + } + } + return false; + } + + private String getExtensionValue(Element element, String url) { + List extensions = element.getChildrenByName("extension"); + for (Element extension : extensions) { + if (url.equals(extension.getNamedChildValue("url"))) { + return extension.getNamedChildValue("value"); + } + } + return null; } private StructureDefinition loadAsSD(Element src) throws FHIRException, IOException { 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/SnapShotGenerationXTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java index 030352c40..8b616af25 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java @@ -334,7 +334,7 @@ public class SnapShotGenerationXTests { } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { if ("fixture".equals(functionName)) { String id = fp.convertToString(parameters.get(0)); Resource res = fetchFixture(id); 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/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java index 7dc58c52c..7c8671c66 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java @@ -435,7 +435,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe } @Override - public List executeFunction(Object appContext, String functionName, List> parameters) { + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { return null; } diff --git a/pom.xml b/pom.xml index 29be4c280..8e975d598 100644 --- a/pom.xml +++ b/pom.xml @@ -13,19 +13,18 @@ each other. It is fine to bump the point version of this POM without affecting HAPI FHIR. --> - 5.1.11-SNAPSHOT + org.hl7.fhir.core + 5.1.17-SNAPSHOT + pom 5.1.0 - 1.1.40 + 1.1.45 5.6.2 3.0.0-M4 0.8.5 - org.hl7.fhir.core - pom - HL7 Core Artifacts @@ -47,13 +46,6 @@ org.hl7.fhir.report - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - @@ -318,17 +310,6 @@ - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - true - - org.apache.maven.plugins maven-deploy-plugin @@ -338,7 +319,23 @@ - deploy + github-repo + + false + + deployToGitHub + + + + + github + https://maven.pkg.github.com/hapifhir/org.hl7.fhir.core + + + github + https://maven.pkg.github.com/hapifhir/org.hl7.fhir.core + + @@ -362,5 +359,57 @@ + + ossrh-repo + + false + + deployToSonatype + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + ${gpg.keyname} + ${gpg.keyname} + + + + + + +