diff --git a/org.hl7.fhir.core.generator/configuration/CapabilityStatement.java b/org.hl7.fhir.core.generator/configuration/CapabilityStatement.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/org.hl7.fhir.core.generator/configuration/ImplementationGuideDefinitionResourceComponent.java b/org.hl7.fhir.core.generator/configuration/ImplementationGuideDefinitionResourceComponent.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/org.hl7.fhir.core.generator/configuration/ManifestResourceComponent.java b/org.hl7.fhir.core.generator/configuration/ManifestResourceComponent.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java index 58c20fa1a..245d74bd8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfilePathProcessor.java @@ -842,6 +842,7 @@ public class ProfilePathProcessor { outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); profileUtilities.updateConstraintSources(outcome, getSourceStructureDefinition().getUrl()); + profileUtilities.checkExtensions(outcome); profileUtilities.updateFromObligationProfiles(outcome); profileUtilities.updateURLs(url, webUrl, outcome); profileUtilities.markDerived(outcome); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java index 5b5b6ef0d..34d9e0966 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java @@ -218,7 +218,14 @@ public class ProfileUtilities extends TranslatingUtilities { ALL_TYPES // allow any unknow profile } - private static final List NON_INHERITED_ED_URLS = Arrays.asList( + /** + * These extensions are stripped in inherited profiles (and may be replaced by + */ + + public static final List NON_INHERITED_ED_URLS = Arrays.asList( + "http://hl7.org/fhir/tools/StructureDefinition/binding-definition", + "http://hl7.org/fhir/tools/StructureDefinition/no-binding", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-isCommonBinding", "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status", "http://hl7.org/fhir/StructureDefinition/structuredefinition-category", "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm", @@ -229,11 +236,62 @@ public class ProfileUtilities extends TranslatingUtilities { "http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version", "http://hl7.org/fhir/tools/StructureDefinition/obligation-profile", "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status-reason", - ToolingExtensions.EXT_SUMMARY, + ToolingExtensions.EXT_SUMMARY/*, ToolingExtensions.EXT_OBLIGATION_CORE, - ToolingExtensions.EXT_OBLIGATION_TOOLS); + ToolingExtensions.EXT_OBLIGATION_TOOLS*/); + public static final List DEFAULT_INHERITED_ED_URLS = Arrays.asList( + "http://hl7.org/fhir/StructureDefinition/questionnaire-optionRestriction", + "http://hl7.org/fhir/StructureDefinition/questionnaire-referenceProfile", + "http://hl7.org/fhir/StructureDefinition/questionnaire-referenceResource", + "http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption", + "http://hl7.org/fhir/StructureDefinition/mimeType"); + + /** + * These extensions are ignored when found in differentials + */ + public static final List NON_OVERRIDING_ED_URLS = Arrays.asList( + "http://hl7.org/fhir/StructureDefinition/elementdefinition-translatable", + "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-json-name", + "http://hl7.org/fhir/tools/StructureDefinition/implied-string-prefix", + "http://hl7.org/fhir/tools/StructureDefinition/json-empty-behavior", + "http://hl7.org/fhir/tools/StructureDefinition/json-nullable", + "http://hl7.org/fhir/tools/StructureDefinition/json-primitive-choice", + "http://hl7.org/fhir/tools/StructureDefinition/json-property-key", + "http://hl7.org/fhir/tools/StructureDefinition/type-specifier", + "http://hl7.org/fhir/tools/StructureDefinition/xml-choice-group", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype" + ); + + /** + * When these extensions are found, they override whatever is set on the ancestor element + */ + public static final List OVERRIDING_ED_URLS = Arrays.asList( + "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-date-format", + ToolingExtensions.EXT_DATE_RULES, + "http://hl7.org/fhir/StructureDefinition/designNote", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits", + "http://hl7.org/fhir/StructureDefinition/elementdefinition-question", + "http://hl7.org/fhir/StructureDefinition/entryFormat", + "http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces", + "http://hl7.org/fhir/StructureDefinition/maxSize", + "http://hl7.org/fhir/StructureDefinition/minLength", + "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation", + "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "http://hl7.org/fhir/StructureDefinition/questionnaire-signatureRequired", + "http://hl7.org/fhir/StructureDefinition/questionnaire-sliderStepValue", + "http://hl7.org/fhir/StructureDefinition/questionnaire-supportLink", + "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "http://hl7.org/fhir/StructureDefinition/questionnaire-unitValueSet", + "http://hl7.org/fhir/StructureDefinition/questionnaire-usageMode", + "http://hl7.org/fhir/StructureDefinition/structuredefinition-display-hint", + "http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name" + ); + public IWorkerContext getContext() { return this.context; } @@ -1937,7 +1995,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (Character.isWhitespace(ch)) { // found the end of the processible link: String url = markdown.substring(linkLeft, i); - if (isLikelySourceURLReference(url, resourceNames, baseFilenames, localFilenames)) { + if (isLikelySourceURLReference(url, resourceNames, baseFilenames, localFilenames, webUrl)) { b.append(basePath); if (!Utilities.noString(basePath) && !basePath.endsWith("/")) { b.append("/"); @@ -1967,7 +2025,7 @@ public class ProfileUtilities extends TranslatingUtilities { // This code is trying to guess which relative references are actually to the // base specification. // - if (isLikelySourceURLReference(url, resourceNames, baseFilenames, localFilenames)) { + if (isLikelySourceURLReference(url, resourceNames, baseFilenames, localFilenames, webUrl)) { b.append("]("); b.append(basePath); if (!Utilities.noString(basePath) && !basePath.endsWith("/")) { @@ -2013,28 +2071,33 @@ public class ProfileUtilities extends TranslatingUtilities { } - private static boolean isLikelySourceURLReference(String url, List resourceNames, Set baseFilenames, Set localFilenames) { - if (resourceNames != null) { - for (String n : resourceNames) { - if (n != null && url.startsWith(n.toLowerCase()+".html")) { - return true; - } - if (n != null && url.startsWith(n.toLowerCase()+"-definitions.html")) { - return true; + private static boolean isLikelySourceURLReference(String url, List resourceNames, Set baseFilenames, Set localFilenames, String baseUrl) { + if (url == null) { + return false; + } + if (baseUrl != null && !baseUrl.startsWith("http://hl7.org/fhir/R")) { + if (resourceNames != null) { + for (String n : resourceNames) { + if (n != null && url.startsWith(n.toLowerCase()+".html")) { + return true; + } + if (n != null && url.startsWith(n.toLowerCase()+"-definitions.html")) { + return true; + } } } - } - if (localFilenames != null) { - for (String n : localFilenames) { - if (n != null && url.startsWith(n.toLowerCase())) { - return false; + if (localFilenames != null) { + for (String n : localFilenames) { + if (n != null && url.startsWith(n.toLowerCase())) { + return false; + } } } - } - if (baseFilenames != null) { - for (String n : baseFilenames) { - if (n != null && url.startsWith(n.toLowerCase())) { - return true; + if (baseFilenames != null) { + for (String n : baseFilenames) { + if (n != null && url.startsWith(n.toLowerCase())) { + return true; + } } } } @@ -2306,20 +2369,12 @@ public class ProfileUtilities extends TranslatingUtilities { dest.getExtension().remove(elist.get(1)); } - for (Extension ext : source.getExtension()) { - if (!Utilities.existsInList(ext.getUrl(), NON_INHERITED_ED_URLS) && !dest.hasExtension(ext.getUrl())) { - dest.getExtension().add(ext.copy()); - } - } - for (Extension ext : source.getExtension()) { - if (Utilities.existsInList(ext.getUrl(), ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) { - dest.getExtension().add(ext.copy()); - } - } + updateExtensionsFromDefinition(dest, source); + for (ElementDefinition ed : obligationProfileElements) { for (Extension ext : ed.getExtension()) { if (Utilities.existsInList(ext.getUrl(), ToolingExtensions.EXT_OBLIGATION_CORE, ToolingExtensions.EXT_OBLIGATION_TOOLS)) { - dest.getExtension().add(ext.copy()); + dest.getExtension().add(new Extension(ToolingExtensions.EXT_OBLIGATION_CORE, ext.getValue().copy())); } } } @@ -2638,6 +2693,7 @@ public class ProfileUtilities extends TranslatingUtilities { hasBinding = hasBinding || ed.hasBinding(); } if (hasBinding) { + updateExtensionsFromDefinition(dest.getBinding(), source.getBinding()); ElementDefinitionBindingComponent binding = derived.getBinding(); for (ElementDefinition ed : obligationProfileElements) { for (Extension ext : ed.getBinding().getExtension()) { @@ -2726,8 +2782,9 @@ public class ProfileUtilities extends TranslatingUtilities { derived.setBinding(null); else derived.getBinding().setUserData(UD_DERIVATION_EQUALS, true); - } // else if (base.hasBinding() && doesn't have bindable type ) - // base + } else if (base.hasBinding()) { + base.getBinding().getExtension().removeIf(ext -> Utilities.existsInList(ext.getUrl(), ProfileUtilities.NON_INHERITED_ED_URLS)); + } if (derived.hasIsSummaryElement()) { if (!Base.compareDeep(derived.getIsSummaryElement(), base.getIsSummaryElement(), false)) { @@ -2820,6 +2877,22 @@ public class ProfileUtilities extends TranslatingUtilities { //updateURLs(url, webUrl, dest); } + private void updateExtensionsFromDefinition(Element dest, Element source) { + dest.getExtension().removeIf(ext -> Utilities.existsInList(ext.getUrl(), NON_INHERITED_ED_URLS) || (Utilities.existsInList(ext.getUrl(), DEFAULT_INHERITED_ED_URLS) && source.hasExtension(ext.getUrl()))); + + for (Extension ext : source.getExtension()) { + if (!dest.hasExtension(ext.getUrl())) { + dest.getExtension().add(ext.copy()); + } else if (Utilities.existsInList(ext.getUrl(), NON_OVERRIDING_ED_URLS)) { + // do nothing + } else if (Utilities.existsInList(ext.getUrl(), OVERRIDING_ED_URLS)) { + dest.getExtensionByUrl(ext.getUrl()).setValue(ext.getValue()); + } else { + dest.getExtension().add(ext.copy()); + } + } + } + private void mergeAdditionalBinding(ElementDefinitionBindingAdditionalComponent dest, ElementDefinitionBindingAdditionalComponent source) { for (UsageContext t : source.getUsage()) { if (!hasUsage(dest, t)) { @@ -4565,4 +4638,11 @@ public class ProfileUtilities extends TranslatingUtilities { return propertyCache; } + public void checkExtensions(ElementDefinition outcome) { + outcome.getExtension().removeIf(ext -> Utilities.existsInList(ext.getUrl(), ProfileUtilities.NON_INHERITED_ED_URLS)); + if (outcome.hasBinding()) { + outcome.getBinding().getExtension().removeIf(ext -> Utilities.existsInList(ext.getUrl(), ProfileUtilities.NON_INHERITED_ED_URLS)); + } + } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java index 7b211b8eb..58fcd84db 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/ContextUtilities.java @@ -435,5 +435,13 @@ public class ContextUtilities implements ProfileKnowledgeProvider { return null; } + public boolean isAbstractType(String typeName) { + StructureDefinition sd = context.fetchTypeDefinition(typeName); + if (sd != null) { + return sd.getAbstract(); + } + return false; + } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Constants.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Constants.java index 8932e3cc6..898624628 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Constants.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Constants.java @@ -43,5 +43,6 @@ public class Constants { public final static String VERSION_MM = "5.0"; public final static String DATE = "Thu, Mar 23, 2023 19:59+1100"; public final static String URI_REGEX = "((http|https):\\/\\/([A-Za-z0-9\\\\\\.\\:\\%\\$\\-]*\\/)*?)?(Account|ActivityDefinition|ActorDefinition|AdministrableProductDefinition|AdverseEvent|AllergyIntolerance|Appointment|AppointmentResponse|ArtifactAssessment|AuditEvent|Basic|Binary|BiologicallyDerivedProduct|BiologicallyDerivedProductDispense|BodyStructure|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|ChargeItemDefinition|Citation|Claim|ClaimResponse|ClinicalImpression|ClinicalUseDefinition|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition|ConditionDefinition|Consent|Contract|Coverage|CoverageEligibilityRequest|CoverageEligibilityResponse|DetectedIssue|Device|DeviceAssociation|DeviceDefinition|DeviceDispense|DeviceMetric|DeviceRequest|DeviceUsage|DiagnosticReport|DocumentReference|Encounter|EncounterHistory|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|EventDefinition|Evidence|EvidenceReport|EvidenceVariable|ExampleScenario|ExplanationOfBenefit|FamilyMemberHistory|Flag|FormularyItem|GenomicStudy|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingSelection|ImagingStudy|Immunization|ImmunizationEvaluation|ImmunizationRecommendation|ImplementationGuide|Ingredient|InsurancePlan|InventoryItem|InventoryReport|Invoice|Library|Linkage|List|Location|ManufacturedItemDefinition|Measure|MeasureReport|Medication|MedicationAdministration|MedicationDispense|MedicationKnowledge|MedicationRequest|MedicationStatement|MedicinalProductDefinition|MessageDefinition|MessageHeader|MolecularSequence|NamingSystem|NutritionIntake|NutritionOrder|NutritionProduct|Observation|ObservationDefinition|OperationDefinition|OperationOutcome|Organization|OrganizationAffiliation|PackagedProductDefinition|Parameters|Patient|PaymentNotice|PaymentReconciliation|Permission|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|Provenance|Questionnaire|QuestionnaireResponse|RegulatedAuthorization|RelatedPerson|RequestOrchestration|Requirements|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|ServiceRequest|Slot|Specimen|SpecimenDefinition|StructureDefinition|StructureMap|Subscription|SubscriptionStatus|SubscriptionTopic|Substance|SubstanceDefinition|SubstanceNucleicAcid|SubstancePolymer|SubstanceProtein|SubstanceReferenceInformation|SubstanceSourceMaterial|SupplyDelivery|SupplyRequest|Task|TerminologyCapabilities|TestPlan|TestReport|TestScript|Transport|ValueSet|VerificationResult|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?"; + public static final String NS_FHIR_ROOT = "http://hl7.org/fhir"; public static final String NS_CDA_ROOT = "http://hl7.org/cda/stds/core"; } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/TypeDetails.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/TypeDetails.java index 5b6609c19..72340c0e6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/TypeDetails.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/TypeDetails.java @@ -62,12 +62,20 @@ public class TypeDetails { public static final Set FP_NUMBERS = new HashSet(Arrays.asList(FP_Integer, FP_Decimal)); public static class ProfiledType { + @Override + public String toString() { + return uri; + } + private String uri; private List profiles; // or, not and private List bindings; public ProfiledType(String n) { - uri = ns(n); + uri = ns(n); + if (uri.equals("http://hl7.org/fhir/StructureDefinition/CDA")) { + System.out.println("!"); // #FIXME + } } public String getUri() { 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 c93c93fa0..25f6b78b8 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 @@ -62,6 +62,7 @@ import org.hl7.fhir.r5.model.TypeDetails; import org.hl7.fhir.r5.model.TypeDetails.ProfiledType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; +import org.hl7.fhir.r5.utils.FHIRPathEngine.IssueMessage; import org.hl7.fhir.r5.utils.FHIRPathUtilityClasses.FHIRConstant; import org.hl7.fhir.r5.utils.FHIRPathUtilityClasses.ClassTypeInfo; import org.hl7.fhir.r5.utils.FHIRPathUtilityClasses.FunctionDetails; @@ -119,6 +120,26 @@ import ca.uhn.fhir.util.ElementUtil; */ public class FHIRPathEngine { + public class IssueMessage { + + private String message; + private String id; + + public IssueMessage(String message, String id) { + this.message = message; + this.id = id; + } + + public String getMessage() { + return message; + } + + public String getId() { + return id; + } + + } + private enum Equality { Null, True, False } private IWorkerContext worker; @@ -136,7 +157,7 @@ public class FHIRPathEngine { private boolean doNotEnforceAsSingletonRule; private boolean doNotEnforceAsCaseSensitive; private boolean allowDoubleQuotes; - private List typeWarnings = new ArrayList<>(); + private List typeWarnings = new ArrayList<>(); private boolean emitSQLonFHIRWarning; // if the fhir path expressions are allowed to use constants beyond those defined in the specification @@ -499,7 +520,7 @@ public class FHIRPathEngine { * @throws PathEngineException * @if the path is not valid */ - public TypeDetails checkOnTypes(Object appContext, String resourceType, List typeList, ExpressionNode expr, List warnings) throws FHIRLexerException, PathEngineException, DefinitionException { + public TypeDetails checkOnTypes(Object appContext, String resourceType, List typeList, ExpressionNode expr, List warnings) throws FHIRLexerException, PathEngineException, DefinitionException { typeWarnings.clear(); // if context is a path that refers to a type, do that conversion now @@ -550,7 +571,7 @@ public class FHIRPathEngine { return res; } - public TypeDetails checkOnTypes(Object appContext, String resourceType, TypeDetails types, ExpressionNode expr, List warnings) throws FHIRLexerException, PathEngineException, DefinitionException { + public TypeDetails checkOnTypes(Object appContext, String resourceType, TypeDetails types, ExpressionNode expr, List warnings) throws FHIRLexerException, PathEngineException, DefinitionException { typeWarnings.clear(); TypeDetails res = executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, null, true, false, expr); warnings.addAll(typeWarnings); @@ -589,9 +610,9 @@ public class FHIRPathEngine { fmt = fmt + " "+worker.formatMessagePlural(num, I18nConstants.FHIRPATH_LOCATION, location); } if (holder != null) { - return new PathEngineException(fmt, holder.getStart(), holder.toString()); + return new PathEngineException(fmt, constName, holder.getStart(), holder.toString()); } else { - return new PathEngineException(fmt); + return new PathEngineException(fmt, constName); } } @@ -601,9 +622,9 @@ public class FHIRPathEngine { fmt = fmt + " "+worker.formatMessage(I18nConstants.FHIRPATH_LOCATION, location); } if (holder != null) { - return new PathEngineException(fmt, holder.getStart(), holder.toString()); + return new PathEngineException(fmt, constName, holder.getStart(), holder.toString()); } else { - return new PathEngineException(fmt); + return new PathEngineException(fmt, constName); } } @@ -1600,10 +1621,10 @@ public class FHIRPathEngine { // special Logic for SQL-on-FHIR: if (focus.isChoice()) { if (expr.getInner() == null || expr.getInner().getFunction() != Function.OfType) { - typeWarnings.add(worker.formatMessage(I18nConstants.FHIRPATH_CHOICE_NO_TYPE_SPECIFIER, expr.toString())); + typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_CHOICE_NO_TYPE_SPECIFIER, expr.toString()), I18nConstants.FHIRPATH_CHOICE_NO_TYPE_SPECIFIER)); } } else if (expr.getInner() != null && expr.getInner().getFunction() == Function.OfType) { - typeWarnings.add(worker.formatMessage(I18nConstants.FHIRPATH_CHOICE_SPURIOUS_TYPE_SPECIFIER, expr.toString())); + typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_CHOICE_SPURIOUS_TYPE_SPECIFIER, expr.toString()), I18nConstants.FHIRPATH_CHOICE_SPURIOUS_TYPE_SPECIFIER)); } } } @@ -1816,10 +1837,10 @@ public class FHIRPathEngine { } else { String tn = convertToString(right); if (!isKnownType(tn)) { - throw new PathEngineException("The type "+tn+" is not valid"); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_INVALID_TYPE, tn), I18nConstants.FHIRPATH_INVALID_TYPE); } if (!doNotEnforceAsSingletonRule && left.size() > 1) { - throw new PathEngineException("Attempt to use as on more than one item ("+left.size()+", '"+expr.toString()+"')"); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_AS_COLLECTION, left.size(), expr.toString()), I18nConstants.FHIRPATH_AS_COLLECTION); } for (Base nextLeft : left) { if (compareTypeNames(tn, nextLeft.fhirType())) { @@ -1904,18 +1925,18 @@ public class FHIRPathEngine { private void checkCardinalityForComparabilitySame(TypeDetails left, Operation operation, TypeDetails right, ExpressionNode expr) { if (left.isList() && !right.isList()) { - typeWarnings.add(worker.formatMessage(I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT, expr.toString())); + typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT, expr.toString()), I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT)); } else if (!left.isList() && right.isList()) { - typeWarnings.add(worker.formatMessage(I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT, expr.toString())); + typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT, expr.toString()), I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT)); } } private void checkCardinalityForSingle(TypeDetails left, Operation operation, TypeDetails right, ExpressionNode expr) { if (left.isList()) { - typeWarnings.add(worker.formatMessage(I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT, expr.toString())); + typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT, expr.toString()), I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT)); } if (right.isList()) { - typeWarnings.add(worker.formatMessage(I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT, expr.toString())); + typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT, expr.toString()), I18nConstants.FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT)); } } @@ -1995,7 +2016,7 @@ public class FHIRPathEngine { 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()); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_ARITHMETIC_PLUS, right.getType(), left.getType()), I18nConstants.FHIRPATH_ARITHMETIC_PLUS, expr.getOpStart(), expr.toString()); } } return result; @@ -2012,7 +2033,7 @@ public class FHIRPathEngine { 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())); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_ARITHMETIC_MINUS, right.getType(), left.getType()), I18nConstants.FHIRPATH_ARITHMETIC_MINUS, expr.getOpStart(), expr.toString()); } } return result; @@ -2654,13 +2675,13 @@ public class FHIRPathEngine { 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())); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_ARITHMETIC_QTY, q.getCode()), I18nConstants.FHIRPATH_ARITHMETIC_QTY, holder.getOpStart(), holder.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()), holder.getOpStart(), holder.toString()); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_ARITHMETIC_QTY, q.getCode()), I18nConstants.FHIRPATH_ARITHMETIC_QTY, holder.getOpStart(), holder.toString()); case "weeks": case "week": case "wk": @@ -2692,7 +2713,7 @@ public class FHIRPathEngine { result.add(Calendar.MILLISECOND, value); break; default: - throw new PathEngineException(String.format("Error in date arithmetic: unrecognized time unit %s", q.getCode())); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_ARITHMETIC_UNIT, q.getCode()), I18nConstants.FHIRPATH_ARITHMETIC_UNIT, holder.getOpStart(), holder.toString()); } return result; } @@ -2730,7 +2751,7 @@ public class FHIRPathEngine { p = worker.getUcumService().multiply(pl, pr); result.add(pairToQty(p)); } catch (UcumException e) { - throw new PathEngineException(e.getMessage(), expr.getOpStart(), expr.toString(), e); + throw new PathEngineException(e.getMessage(), null, expr.getOpStart(), expr.toString(), e); // #FIXME } } else { throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "*", left.get(0).fhirType(), right.get(0).fhirType()); @@ -3221,7 +3242,7 @@ public class FHIRPathEngine { } if (exp.getFunction() == Function.First || exp.getFunction() == Function.Last || exp.getFunction() == Function.Tail || exp.getFunction() == Function.Skip || exp.getFunction() == Function.Take) { if (focus.getCollectionStatus() == CollectionStatus.SINGLETON) { - typeWarnings.add(worker.formatMessage(I18nConstants.FHIRPATH_NOT_A_COLLECTION, container.toString())); + typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_NOT_A_COLLECTION, container.toString()), I18nConstants.FHIRPATH_NOT_A_COLLECTION)); } } @@ -3287,7 +3308,8 @@ public class FHIRPathEngine { } case As : { checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); - TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); + String tn = checkType(focus, exp); + TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON, tn); if (td.typesHaveTargets()) { td.addTargets(focus.getTargets()); } @@ -3295,10 +3317,7 @@ public class FHIRPathEngine { } case OfType : { checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); - String tn = exp.getParameters().get(0).getName(); - if (typeCastIsImpossible(focus, tn)) { - typeWarnings.add(worker.formatMessage(I18nConstants.FHIRPATH_OFTYPE_IMPOSSIBLE, focus.describeMin(), tn, exp.toString())); - } + String tn = checkType(focus, exp); TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON, tn); if (td.typesHaveTargets()) { td.addTargets(focus.getTargets()); @@ -3618,6 +3637,27 @@ public class FHIRPathEngine { throw new Error("not Implemented yet"); } + private String checkType(TypeDetails focus, ExpressionNode exp) { + String tn; + if (exp.getParameters().get(0).getInner() != null) { + tn = exp.getParameters().get(0).getName()+"."+exp.getParameters().get(0).getInner().getName(); + } else { + tn = "FHIR."+exp.getParameters().get(0).getName(); + } + if (tn.startsWith("System.")) { + tn = tn.substring(7); + } else if (tn.startsWith("FHIR.")) { + tn = Utilities.pathURL(Constants.NS_FHIR_ROOT, "StructureDefinition", tn.substring(5)); + } else if (tn.startsWith("CDA.")) { + tn = Utilities.pathURL(Constants.NS_CDA_ROOT, "StructureDefinition", tn.substring(4)); + } + + if (typeCastIsImpossible(focus, tn)) { + typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_OFTYPE_IMPOSSIBLE, focus.describeMin(), tn, exp.toString()), I18nConstants.FHIRPATH_OFTYPE_IMPOSSIBLE)); + } + return tn; + } + private boolean typeCastIsImpossible(TypeDetails focus, String tn) { return !focus.hasType(tn); } @@ -4871,10 +4911,10 @@ public class FHIRPathEngine { tn = "FHIR."+expr.getParameters().get(0).getName(); } if (!isKnownType(tn)) { - throw new PathEngineException("The type "+tn+" is not valid"); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_INVALID_TYPE, tn), I18nConstants.FHIRPATH_INVALID_TYPE); // #FIXME } if (!doNotEnforceAsSingletonRule && focus.size() > 1) { - throw new PathEngineException("Attempt to use as() on more than one item ("+focus.size()+")"); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_AS_COLLECTION, focus.size(), expr.toString()), I18nConstants.FHIRPATH_AS_COLLECTION); // #FIXME } for (Base b : focus) { @@ -4914,7 +4954,7 @@ public class FHIRPathEngine { tn = "FHIR."+expr.getParameters().get(0).getName(); } if (!isKnownType(tn)) { - throw new PathEngineException("The type "+tn+" is not valid"); + throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_INVALID_TYPE, tn), I18nConstants.FHIRPATH_INVALID_TYPE); // #FIXME } @@ -5967,7 +6007,7 @@ public class FHIRPathEngine { for (TypeRefComponent t : ed.getType()) { if (t.hasCode() && t.getCodeElement().hasValue()) { String tn = null; - if (t.getCode().equals("Element") || t.getCode().equals("BackboneElement")) { + if (Utilities.existsInList(t.getCode(), "Element", "BackboneElement", "Base") || cu.isAbstractType(t.getCode())) { tn = sdi.getType()+"#"+ed.getPath(); } else { tn = t.getCode(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/OperationOutcomeUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/OperationOutcomeUtilities.java index 803d54369..03989d4f6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/OperationOutcomeUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/OperationOutcomeUtilities.java @@ -54,10 +54,12 @@ public class OperationOutcomeUtilities { issue.addExpression(message.getLocation()); } // pass through line/col if they're present - if (message.getLine() >= 0) + if (message.getLine() >= 0) { issue.addExtension().setUrl(ToolingExtensions.EXT_ISSUE_LINE).setValue(new IntegerType(message.getLine())); - if (message.getCol() >= 0) + } + if (message.getCol() >= 0) { issue.addExtension().setUrl(ToolingExtensions.EXT_ISSUE_COL).setValue(new IntegerType(message.getCol())); + } issue.setSeverity(convert(message.getLevel())); CodeableConcept c = new CodeableConcept(); c.setText(message.getMessage()); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java index 7fc69b84d..ca7ffe41d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/sql/Validator.java @@ -12,6 +12,7 @@ import org.hl7.fhir.r5.model.TypeDetails; import org.hl7.fhir.r5.model.ExpressionNode; import org.hl7.fhir.r5.model.ExpressionNode.CollectionStatus; import org.hl7.fhir.r5.utils.FHIRPathEngine; +import org.hl7.fhir.r5.utils.FHIRPathEngine.IssueMessage; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.json.model.JsonArray; import org.hl7.fhir.utilities.json.model.JsonBoolean; @@ -253,7 +254,7 @@ public class Validator { } else { String expr = expression.asString(); - List warnings = new ArrayList<>(); + List warnings = new ArrayList<>(); TypeDetails td = null; ExpressionNode node = null; try { @@ -264,8 +265,8 @@ public class Validator { error(path, expression, e.getMessage(), IssueType.INVALID); } if (td != null && node != null) { - for (String s : warnings) { - warning(path+".path", expression, s); + for (IssueMessage s : warnings) { + warning(path+".path", expression, s.getMessage()); } String columnName = null; JsonElement nameJ = column.get("name"); @@ -419,7 +420,7 @@ public class Validator { } else { String expr = expression.asString(); - List warnings = new ArrayList<>(); + List warnings = new ArrayList<>(); TypeDetails td = null; try { ExpressionNode n = fpe.parse(expr); @@ -429,8 +430,8 @@ public class Validator { error(path, expression, e.getMessage(), IssueType.INVALID); } if (td != null) { - for (String s : warnings) { - warning(path+".forEach", expression, s); + for (IssueMessage s : warnings) { + warning(path+".forEach", expression, s.getMessage()); } } return td; @@ -444,7 +445,7 @@ public class Validator { } else { String expr = expression.asString(); - List warnings = new ArrayList<>(); + List warnings = new ArrayList<>(); TypeDetails td = null; try { ExpressionNode n = fpe.parse(expr); @@ -454,8 +455,8 @@ public class Validator { error(path, expression, e.getMessage(), IssueType.INVALID); } if (td != null) { - for (String s : warnings) { - warning(path+".forEachOrNull", expression, s); + for (IssueMessage s : warnings) { + warning(path+".forEachOrNull", expression, s.getMessage()); } } return td; @@ -546,7 +547,7 @@ public class Validator { error(path, where, "No path provided", IssueType.REQUIRED); } List types = new ArrayList<>(); - List warnings = new ArrayList<>(); + List warnings = new ArrayList<>(); types.add(resourceName); TypeDetails td = null; try { @@ -560,8 +561,8 @@ public class Validator { if (td.getCollectionStatus() != CollectionStatus.SINGLETON || td.getTypes().size() != 1 || !td.hasType("boolean")) { error(path+".path", where.get("path"), "A where path must return a boolean, but the expression "+expr+" returns a "+td.describe(), IssueType.BUSINESSRULE); } else { - for (String s : warnings) { - warning(path+".path", where.get("path"), s); + for (IssueMessage s : warnings) { + warning(path+".path", where.get("path"), s.getMessage()); } } } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ProfileUtilitiesTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ProfileUtilitiesTests.java deleted file mode 100644 index e1b9987ad..000000000 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ProfileUtilitiesTests.java +++ /dev/null @@ -1,906 +0,0 @@ -package org.hl7.fhir.r5.test; - -import java.util.ArrayList; -import java.util.List; - -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.model.Base; -import org.hl7.fhir.r5.model.ElementDefinition; -import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; -import org.hl7.fhir.r5.model.IntegerType; -import org.hl7.fhir.r5.model.StructureDefinition; -import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; -import org.hl7.fhir.r5.test.utils.TestingUtilities; -import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.validation.ValidationMessage; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class ProfileUtilitiesTests { - - // /** -// * This is simple: we just create an empty differential, generate the snapshot, and then insist it must match the base -// * -// * @param context2 -// * @ -// * @throws EOperationOutcome -// */ - @Test - public void testSimple() throws FHIRException { - - StructureDefinition focus = new StructureDefinition(); - StructureDefinition base = TestingUtilities.getSharedWorkerContext().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); - focus.setUrl(Utilities.makeUuidUrn()); - focus.setBaseDefinition(base.getUrl()); - focus.setType("Patient"); - focus.setDerivation(TypeDerivationRule.CONSTRAINT); - List messages = new ArrayList<>(); - new ProfileUtilities(TestingUtilities.getSharedWorkerContext(), messages, null).generateSnapshot(base, focus, focus.getUrl(), "http://test.org/test", "Simple Test"); - - boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size(); - for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { - ElementDefinition b = base.getSnapshot().getElement().get(i); - ElementDefinition f = focus.getSnapshot().getElement().get(i); - if (ok) { - if (!f.hasBase()) - ok = false; - else if (!b.getPath().equals(f.getPath())) - ok = false; - else { - b.setBase(null); - f.setBase(null); - b.setRequirements(null); - f.setRequirements(null); - for (ElementDefinitionConstraintComponent c : b.getConstraint()) { - c.setSource(null); - } - for (ElementDefinitionConstraintComponent c : f.getConstraint()) { - c.setSource(null); - } - ok = Base.compareDeep(b, f, true); - } - } - } - - Assertions.assertTrue(ok); - } - - - // -// /** -// * This is simple: we just create an empty differential, generate the snapshot, and then insist it must match the base. for a different resource with recursion -// * -// * @param context2 -// * @ -// * @throws EOperationOutcome -// */ - @Test - public void testSimple2() { - StructureDefinition base = TestingUtilities.getSharedWorkerContext().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/ValueSet").copy(); - StructureDefinition focus = base.copy(); - focus.setUrl(Utilities.makeUuidUrn()); - focus.setSnapshot(null); - focus.setDifferential(null); - List messages = new ArrayList<>(); - new ProfileUtilities(TestingUtilities.getSharedWorkerContext(), messages, null).generateSnapshot(base, focus, focus.getUrl(), "http://test.org", "Simple Test"); - - boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size(); - for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { - if (ok) { - ElementDefinition b = base.getSnapshot().getElement().get(i); - ElementDefinition f = focus.getSnapshot().getElement().get(i); - for (ElementDefinitionConstraintComponent c : b.getConstraint()) { - c.setSource(null); - } - for (ElementDefinitionConstraintComponent c : f.getConstraint()) { - c.setSource(null); - } - if (!f.hasBase() || !b.getPath().equals(f.getPath())) - ok = false; - else { - f.setBase(null); - b.setBase(null); - b.setRequirements(null); - f.setRequirements(null); - b.setComment(null); - f.setComment(null); - b.setDefinition(null); - f.setDefinition(null); - b.setContentReference(null); - f.setContentReference(null); - ok = Base.compareDeep(b, f, true); - } - } - } - - Assertions.assertTrue(ok); - } - - /** - * Change one cardinality. - */ - @Test - void testCardinalityChange() { - StructureDefinition focus = new StructureDefinition(); - StructureDefinition base = TestingUtilities.getSharedWorkerContext().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); - focus.setUrl(Utilities.makeUuidUrn()); - focus.setBaseDefinition(base.getUrl()); - focus.setType(base.getType()); - focus.setDerivation(TypeDerivationRule.CONSTRAINT); - ElementDefinition id = focus.getDifferential().addElement(); - id.setPath("Patient.identifier"); - id.setMin(1); - List messages = new ArrayList<>(); - new ProfileUtilities(TestingUtilities.getSharedWorkerContext(), messages, null).generateSnapshot(base, focus, focus.getUrl(), "http://test.org", "Simple Test"); - - boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size(); - for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { - if (ok) { - ElementDefinition b = base.getSnapshot().getElement().get(i); - ElementDefinition f = focus.getSnapshot().getElement().get(i); - b.setRequirements(null); - f.setRequirements(null); - for (ElementDefinitionConstraintComponent c : b.getConstraint()) { - c.setSource(null); - } - for (ElementDefinitionConstraintComponent c : f.getConstraint()) { - c.setSource(null); - } - if (!f.hasBase() || !b.getPath().equals(f.getPath())) { - ok = false; - } - else { - if (f.getPath().equals("Patient.identifier")) { - ok = f.getMin() == 1; - if (ok) { - f.setMin(0); - } - } - if (!Base.compareDeep(b, f, true)) { - ok = Base.compareDeep(b, f, true); - } - } - } - } - - Assertions.assertTrue(ok); - } - - /** - * Change min value - */ - @Test - void testMinValueChange() { - // Given - StructureDefinition focus = new StructureDefinition(); - StructureDefinition base = TestingUtilities.getSharedWorkerContext().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Appointment").copy(); - focus.setUrl(Utilities.makeUuidUrn()); - focus.setBaseDefinition(base.getUrl()); - focus.setType(base.getType()); - focus.setDerivation(TypeDerivationRule.CONSTRAINT); - ElementDefinition id = focus.getDifferential().addElement(); - id.setPath("Appointment.minutesDuration"); - id.setMinValue(new IntegerType(1)); - List messages = new ArrayList<>(); - // When - new ProfileUtilities(TestingUtilities.getSharedWorkerContext(), messages, null).generateSnapshot(base, focus, focus.getUrl(), "http://test.org", "Simple Test"); - // Then - boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size(); - for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { - if (ok) { - ElementDefinition b = base.getSnapshot().getElement().get(i); - ElementDefinition f = focus.getSnapshot().getElement().get(i); - b.setRequirements(null); - f.setRequirements(null); - for (ElementDefinitionConstraintComponent c : b.getConstraint()) { - c.setSource(null); - } - for (ElementDefinitionConstraintComponent c : f.getConstraint()) { - c.setSource(null); - } - if (!f.hasBase() || !b.getPath().equals(f.getPath())) { - ok = false; - } - else { - if (f.getPath().equals("Appointment.minutesDuration")) { - ok = f.getMinValue() instanceof IntegerType && ((IntegerType) f.getMinValue()).getValue() == 1; - if (ok) { - // Can't set minValue to null so change base minValue to IntegerType(1) - b.setMinValue(new IntegerType(1)); - } - } - if (!Base.compareDeep(b, f, true)) { - ok = false; - } - } - } - } - - Assertions.assertTrue(ok); - } - - /** - * Change max value - */ - @Test - void testMaxValueChange() { - // Given - StructureDefinition focus = new StructureDefinition(); - StructureDefinition base = TestingUtilities.getSharedWorkerContext().fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Appointment").copy(); - focus.setUrl(Utilities.makeUuidUrn()); - focus.setBaseDefinition(base.getUrl()); - focus.setType(base.getType()); - focus.setDerivation(TypeDerivationRule.CONSTRAINT); - ElementDefinition id = focus.getDifferential().addElement(); - id.setPath("Appointment.minutesDuration"); - id.setMaxValue(new IntegerType(1)); - List messages = new ArrayList<>(); - // When - new ProfileUtilities(TestingUtilities.getSharedWorkerContext(), messages, null).generateSnapshot(base, focus, focus.getUrl(), "http://test.org", "Simple Test"); - // Then - boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size(); - for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { - if (ok) { - ElementDefinition b = base.getSnapshot().getElement().get(i); - ElementDefinition f = focus.getSnapshot().getElement().get(i); - b.setRequirements(null); - f.setRequirements(null); - for (ElementDefinitionConstraintComponent c : b.getConstraint()) { - c.setSource(null); - } - for (ElementDefinitionConstraintComponent c : f.getConstraint()) { - c.setSource(null); - } - if (!f.hasBase() || !b.getPath().equals(f.getPath())) { - ok = false; - } - else { - if (f.getPath().equals("Appointment.minutesDuration")) { - ok = f.getMaxValue() instanceof IntegerType && ((IntegerType) f.getMaxValue()).getValue() == 1; - if (ok) { - // Can't set maxValue to null so change base maxValue to IntegerType(1) - b.setMaxValue(new IntegerType(1)); - } - } - if (!Base.compareDeep(b, f, true)) { - ok = false; - } - } - } - } - - Assertions.assertTrue(ok); - } - -// -// /** -// * check that documentation appending is working -// * -// * @param context2 -// * @ -// * @throws EOperationOutcome -// */ -// private void testDocumentationAppend() throws EOperationOutcome, Exception { -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// ElementDefinition id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier"); -// id.setDefinition("... some more doco"); -// List messages = new ArrayList(); -// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size(); -// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { -// if (ok) { -// ElementDefinition b = base.getSnapshot().getElement().get(i); -// ElementDefinition f = focus.getSnapshot().getElement().get(i); -// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath())) -// ok = false; -// else { -// f.setBase(null); -// if (f.getPath().equals("Patient.identifier")) { -// ok = f.getDefinition().length() > b.getDefinition().length(); -// if (ok) { -// f.setDefinition(null); -// b.setDefinition(null); -// } -// } -// ok = ok && Base.compareDeep(b, f, true); -// } -// } -// } -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation documentation append failed"); -// } else -// System.out.println("Snap shot generation documentation append test passed"); -// } -// -// -// /** -// * check that narrowing types is working -// * this one doesn't rename the path -// * -// * @param context2 -// * @ -// * @throws EOperationOutcome -// */ -// private void textTypeNarrowing1() throws EOperationOutcome, Exception { -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// ElementDefinition id = focus.getDifferential().addElement(); -// id.setPath("Patient.deceased[x]"); -// id.addType().setCode("dateTime"); -// List messages = new ArrayList(); -// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size(); -// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { -// if (ok) { -// ElementDefinition b = base.getSnapshot().getElement().get(i); -// ElementDefinition f = focus.getSnapshot().getElement().get(i); -// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath())) -// ok = false; -// else { -// f.setBase(null); -// if (f.getPath().equals("Patient.deceasedDateTime")) { -// ok = f.getType().size() == 1 && f.getType().get(0).getCode().equals("dateTime"); -// if (ok) { -// f.getType().clear(); -// b.getType().clear(); -// f.setPath(b.getPath()); -// } -// } -// ok = ok && Base.compareDeep(b, f, true); -// } -// } -// } -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation narrow type 1 failed"); -// } else -// System.out.println("Snap shot generation narrow type 1 test passed"); -// } -// -// /** -// * check that narrowing types is working -// * this one renames the path -// * -// * @param context2 -// * @ -// * @throws EOperationOutcome -// */ -// private void textTypeNarrowing2() throws EOperationOutcome, Exception { -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// ElementDefinition id = focus.getDifferential().addElement(); -// id.setPath("Patient.deceasedDateTime"); -// id.addType().setCode("dateTime"); -// List messages = new ArrayList(); -// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size(); -// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { -// if (ok) { -// ElementDefinition b = base.getSnapshot().getElement().get(i); -// ElementDefinition f = focus.getSnapshot().getElement().get(i); -// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath())) -// ok = false; -// else { -// f.setBase(null); -// if (f.getPath().equals("Patient.deceasedDateTime")) { -// ok = f.getType().size() == 1 && f.getType().get(0).getCode().equals("dateTime"); -// if (ok) { -// f.getType().clear(); -// b.getType().clear(); -// f.setPath(b.getPath()); -// } -// } -// ok = ok && Base.compareDeep(b, f, true); -// } -// } -// } -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation narrow type 2 failed"); -// } else -// System.out.println("Snap shot generation narrow type 2 test passed"); -// } -// -// /** -// * check that mapping resolution is working -// * -// * @param context2 -// * @ -// * @throws EOperationOutcome -// */ -// private void testMapping() throws EOperationOutcome, Exception { -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// ElementDefinition id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier"); -// id.addMapping().setIdentity("rim").setMap("test"); -// List messages = new ArrayList(); -// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size(); -// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { -// if (ok) { -// ElementDefinition b = base.getSnapshot().getElement().get(i); -// ElementDefinition f = focus.getSnapshot().getElement().get(i); -// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath())) -// ok = false; -// else { -// f.setBase(null); -// if (f.getPath().equals("Patient.identifier")) { -// ok = f.getMapping().size() > b.getMapping().size(); -// if (ok) { -// f.getMapping().clear(); -// b.getMapping().clear(); -// } -// } -// ok = ok && Base.compareDeep(b, f, true); -// } -// } -// } -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation mapping changes failed"); -// } else -// System.out.println("Snap shot generation mapping changes test passed"); -// } -// -// /** -// * Walking into a type -// * -// * @param context2 -// * @ -// * @throws EOperationOutcome -// */ -// private void testTypeWalk() throws EOperationOutcome, Exception { -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// ElementDefinition id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier"); -// id.setMustSupport(true); -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier.system"); -// id.setMustSupport(true); -// List messages = new ArrayList(); -// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// // the derived should be 8 longer -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 8; -// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { -// if (ok) { -// ElementDefinition b = base.getSnapshot().getElement().get(i); -// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 9 ? i : i + 8); -// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath())) -// ok = false; -// else { -// f.setBase(null); -// if (f.getPath().equals("Patient.identifier")) { -// ok = f.getMustSupport() && !b.getMustSupport(); -// if (ok) { -// f.setMustSupportElement(null); -// } -// } -// ok = Base.compareDeep(b, f, true); -// } -// } -// } -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation simple test failed"); -// } else -// System.out.println("Snap shot generation simple test passed"); -// } -// -// /** -// * Walking into a type, without explicitly doing so -// * -// * note: this currently fails. -// * -// * @param context2 -// * @ -// * @throws EOperationOutcome -// */ -// private void testTypeWalk2() throws EOperationOutcome, Exception { -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// ElementDefinition id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier.system"); -// id.setMustSupport(true); -// List messages = new ArrayList(); -// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// // the derived should be 8 longer -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 8; -// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { -// if (ok) { -// ElementDefinition b = base.getSnapshot().getElement().get(i); -// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 9 ? i : i + 8); -// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath())) -// ok = false; -// else { -// f.setBase(null); -// if (f.getPath().equals("Patient.identifier")) { -// ok = f.getMustSupport() && !b.getMustSupport(); -// if (ok) { -// f.setMustSupportElement(null); -// } -// } -// ok = Base.compareDeep(b, f, true); -// } -// } -// } -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation simple test failed"); -// } else -// System.out.println("Snap shot generation simple test passed"); -// } -// -// -// /** -// * we're going to slice Patient.identifier -// */ -// private void testSlicingSimple() throws EOperationOutcome, Exception { -// -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// -// // set the slice up -// ElementDefinition id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier"); -// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("use").setType(DiscriminatorType.VALUE); -// -// // first slice: -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier"); -// id.setSliceName("name1"); -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier.use"); -// id.setFixed(new CodeType("usual")); -// -// // second slice: -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier"); -// id.setSliceName("name2"); -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.identifier.use"); -// id.setFixed(new CodeType("official")); -// -// -// List messages = new ArrayList(); -// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// // 18 different: identifier + 8 inner children * 2 -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 18; -// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { -// if (ok) { -// ElementDefinition b = base.getSnapshot().getElement().get(i); -// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 9 ? i : i + 18); -// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath())) -// ok = false; -// else { -// f.setBase(null); -// if (f.getPath().equals("Patient.identifier")) { -// ok = f.hasSlicing(); -// if (ok) -// f.setSlicing(null); -// } -// ok = Base.compareDeep(b, f, true); -// } -// } -// } -// // now, check that the slices we skipped are correct: -// for (int i = 10; i <= 18; i++) { -// if (ok) { -// ElementDefinition d1 = focus.getSnapshot().getElement().get(i); -// ElementDefinition d2 = focus.getSnapshot().getElement().get(i+9); -// if (d1.getPath().equals("Patient.identifier.use")) { -// ok = d1.hasFixed() && d2.hasFixed() && !Base.compareDeep(d1.getFixed(), d2.getFixed(), true); -// if (ok) { -// d1.setFixed(null); -// d2.setFixed(null); -// } -// } -// if (d1.getPath().equals("Patient.identifier")) { -// ok = d1.hasSliceName() && d2.hasSliceName() && !Base.compareDeep(d1.getSliceNameElement(), d2.getSliceNameElement(), true); -// if (ok) { -// d1.setSliceName(null); -// d2.setSliceName(null); -// } -// } -// ok = Base.compareDeep(d1, d2, true); -// } -// } -// // for throughness, we could check against identifier too, but this is not done now. -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation slicing failed"); -// } else -// System.out.println("Snap shot generation slicing passed"); -// -// } -// -// /** -// * we're going to slice Patient.extension and refer to extension by profile -// * -// * implicit: whether to rely on implicit extension slicing -// */ -// private void testSlicingExtension(boolean implicit) throws EOperationOutcome, Exception { -// -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// -// // set the slice up -// ElementDefinition id; -// if (!implicit) { -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.extension"); -// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE); -// id.setMax("3"); -// } -// // first slice: -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.extension"); -// id.setSliceName("name1"); -// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-birthTime"); -// id.setMin(1); -// -// // second slice: -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.extension"); -// id.setSliceName("name2"); -// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName"); -// -// List messages = new ArrayList(); -// ProfileUtilities pu = new ProfileUtilities(context, messages, null); -// pu.generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// // 2 different: extension slices -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 2; -// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { -// if (ok) { -// ElementDefinition b = base.getSnapshot().getElement().get(i); -// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 7 ? i : i + 2); -// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath())) -// ok = false; -// else { -// f.setBase(null); -// if (f.getPath().equals("Patient.extension")) { -// ok = f.hasSlicing() && (implicit || f.getMax().equals("3")); -// if (ok) { -// f.setSlicing(null); -// f.setMaxElement(b.getMaxElement()); -// } -// } -// if (!f.getPath().equals("Patient.extension")) // no compare that because the definitions get overwritten -// ok = Base.compareDeep(b, f, true); -// } -// } -// } -// // now, check that the slices we skipped are correct: -// if (ok) { -// ElementDefinition d1 = focus.getSnapshot().getElement().get(8); -// ElementDefinition d2 = focus.getSnapshot().getElement().get(9); -// ok = d1.hasType() && d1.getType().get(0).hasProfile() && d2.hasType() && d2.getType().get(0).hasProfile() && !Base.compareDeep(d1.getType(), d2.getType(), true) && -// d1.getMin() == 1 && d2.getMin() == 0 && d1.getMax().equals("1") && d2.getMax().equals("1"); -// if (ok) { -// d1.getType().clear(); -// d2.getType().clear(); -// d1.setSliceName("x"); -// d2.setSliceName("x"); -// d1.setMin(0); -// } -// ok = Base.compareDeep(d1, d2, true); -// // for throughness, we could check against extension too, but this is not done now. -// } -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation slicing extensions simple ("+(implicit ? "implicit" : "not implicit")+") failed"); -// } else -// System.out.println("Snap shot generation slicing extensions simple ("+(implicit ? "implicit" : "not implicit")+") passed"); -// } -// -// /** -// * we're going to slice Patient.extension and refer to extension by profile. one of the extensions is complex, and we're going to walk into -// * it and make it must support -// * -// * implicit: whether to rely on implicit extension slicing -// */ -// private void testSlicingExtensionComplex(boolean implicit) throws EOperationOutcome, Exception { -// -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// -// // set the slice up -// ElementDefinition id; -// if (!implicit) { -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.extension"); -// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE); -// } -// // first slice - a simple one to get us going: -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.extension"); -// id.setSliceName("simple"); -// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-birthTime"); -// -// // second slice - the complex one -// // we walk into this and fix properties on the inner extensions -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.extension"); -// id.setSliceName("complex"); -// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-nationality"); -// if (!implicit) { -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.extension.extension"); -// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE); -// } -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.extension.extension"); -// id.setSliceName("code"); -// id.setMustSupport(true); -// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-nationality#code"); -// -// id = focus.getDifferential().addElement(); -// id.setPath("Patient.extension.extension"); -// id.setSliceName("period"); -// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-nationality#period"); -// id.setMax("0"); // prohibit this one.... -// -// List messages = new ArrayList(); -// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// // ok, there's going to 1 (simple) + complex: 1 + id + extnesion.slice + extension.code + (4 inside from that) + extension.period + (4 inside from that) + value + url = 16 -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 16; -// -// // custom checks -// ok = ok && rule(focus.getSnapshot().getElement().get(7).getPath().equals("Patient.extension"), "element 7 (base) path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(7).hasSlicing(), "element 7 slicing"); -// ok = ok && rule(focus.getSnapshot().getElement().get(8).getPath().equals("Patient.extension"), "element 8 (1st slice) path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(8).getSliceName().equals("simple"), "element 8 (1st slice) name"); -// ok = ok && rule(focus.getSnapshot().getElement().get(8).getType().get(0).getProfile().equals("http://hl7.org/fhir/StructureDefinition/patient-birthTime"), "element 9 (2nd slice) profile name"); -// ok = ok && rule(focus.getSnapshot().getElement().get(9).getPath().equals("Patient.extension"), "element 9 (2nd slice) path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(9).getSliceName().equals("complex"), "element 8 (1st slice) name"); -// ok = ok && rule(focus.getSnapshot().getElement().get(9).getType().get(0).getProfile().equals("http://hl7.org/fhir/StructureDefinition/patient-nationality"), "element 9 (2nd slice) profile name"); -// ok = ok && rule(focus.getSnapshot().getElement().get(10).getPath().equals("Patient.extension.id"), "element 10 (2nd slice).id path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(11).getPath().equals("Patient.extension.extension"), "element 11 (2nd slice).extension path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(12).getPath().equals("Patient.extension.extension"), "element 12 (2nd slice).extension path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(12).getMustSupport(), "element 12 (2nd slice).extension must support"); -// ok = ok && rule(focus.getSnapshot().getElement().get(13).getPath().equals("Patient.extension.extension.id"), "element 13 (2nd slice).extension.id path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(14).getPath().equals("Patient.extension.extension.extension"), "element 14 (2nd slice).extension.extension path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(15).getPath().equals("Patient.extension.extension.url"), "element 15 (2nd slice).extension.url path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(16).getPath().equals("Patient.extension.extension.valueCodeableConcept"), "element 16 (2nd slice).extension.valueCodeableConcept path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(17).getPath().equals("Patient.extension.extension"), "element 17 (2nd slice).extension path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(17).getMax().equals("0"), "element 17 (2nd slice).extension cardinality"); -// ok = ok && rule(focus.getSnapshot().getElement().get(18).getPath().equals("Patient.extension.extension.id"), "element 18 (2nd slice).extension.id path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(19).getPath().equals("Patient.extension.extension.extension"), "element 19 (2nd slice).extension.extension path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(20).getPath().equals("Patient.extension.extension.url"), "element 20 (2nd slice).extension.url path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(21).getPath().equals("Patient.extension.extension.valuePeriod"), "element 21 (2nd slice).extension.valuePeriod path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(22).getPath().equals("Patient.extension.url"), "element 22 (2nd slice).url path"); -// ok = ok && rule(focus.getSnapshot().getElement().get(23).getPath().equals("Patient.extension.value[x]"), "element 23 (2nd slice).url path"); -// -// for (int i = 0; i < base.getSnapshot().getElement().size(); i++) { -// if (ok) { -// ElementDefinition b = base.getSnapshot().getElement().get(i); -// ElementDefinition f = focus.getSnapshot().getElement().get(i <= 7 ? i : i + 16); -// if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath())) -// ok = false; -// else { -// f.setBase(null); -// if (f.getPath().equals("Patient.extension")) { -// ok = f.hasSlicing(); -// if (ok) -// f.setSlicing(null); -// } -// if (!f.getPath().equals("Patient.extension")) // no compare that because the definitions get overwritten -// ok = Base.compareDeep(b, f, true); -// } -// } -// } -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation slicing extensions complex ("+(implicit ? "implicit" : "not implicit")+") failed"); -// } else -// System.out.println("Snap shot generation slicing extensions complex ("+(implicit ? "implicit" : "not implicit")+") passed"); -// } -// -// private void testSlicingTask8742() throws EOperationOutcome, Exception { -// StructureDefinition focus = new StructureDefinition(); -// StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Organization").copy(); -// focus.setUrl(Utilities.makeUuidUrn()); -// focus.setBaseDefinition(base.getUrl()); -// focus.setType(base.getType()); -// focus.setDerivation(TypeDerivationRule.CONSTRAINT); -// -// ElementDefinition id = focus.getDifferential().addElement(); -// id.setPath("Organization.address"); -// id.setMin(1); -// id.setMax("1"); -// id.setMustSupport(true); -// -// id = focus.getDifferential().addElement(); -// id.setPath("Organization.address.extension"); -// id.setSliceName("USLabCountycodes"); -// id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE); -// id.setShort("County/Parish FIPS codes"); -// id.setDefinition("County/Parish FIPS codes."); -// id.setRequirements("County/Parish Code SHALL use FIPS 6-4 ( INCITS 31:2009)."); -// id.setMin(0); -// id.setMax("1"); -// id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/us-core-county"); -// id.setMustSupport(true); -// id.getBinding().setStrength(BindingStrength.REQUIRED).setDescription("FIPS codes for US counties and county equivalent entities.").setValueSet(new Reference().setReference("http://hl7.org/fhir/ValueSet/fips-county")); -// List messages = new ArrayList(); -// -// new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test" ); -// -// // 14 for address with one sliced extension -// boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 13; -// -// if (!ok) { -// compareXml(base, focus); -// throw new FHIRException("Snap shot generation test 8742 failed"); -// } else -// System.out.println("Snap shot generation test 8742 passed"); -// } -// -// -// private boolean rule(boolean ok, String message) { -// if (!ok) -// System.out.println("Test failed: " + message); -// return ok; -// } -// - -} 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 b26a5ce16..1368d9f6d 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 @@ -39,10 +39,8 @@ public class PathEngineException extends FHIRException { private static final long serialVersionUID = 31969342112856390L; private SourceLocation location; private String expression; - - public PathEngineException() { - super(); - } + private String id; + public PathEngineException(String message, Throwable cause) { super(message, cause); @@ -59,6 +57,26 @@ public class PathEngineException extends FHIRException { public PathEngineException(String message, SourceLocation location, String expression) { super(message+rep(location, expression)); } + + public PathEngineException(String message, String id, Throwable cause) { + super(message, cause); + this.id = id; + } + + public PathEngineException(String message, String id) { + super(message); + this.id = id; + } + + public PathEngineException(String message, String id, SourceLocation location, String expression, Throwable cause) { + super(message+rep(location, expression), cause); + this.id = id; + } + + public PathEngineException(String message, String id, SourceLocation location, String expression) { + super(message+rep(location, expression)); + this.id = id; + } private static String rep(SourceLocation loc, String expr) { if (loc != null) { @@ -94,4 +112,8 @@ public class PathEngineException extends FHIRException { this.location = location; } + public String getId() { + return id; + } + } \ No newline at end of file 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 19c6364bf..60463d324 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 @@ -952,6 +952,7 @@ public class I18nConstants { public static final String FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT = "FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT"; public static final String ED_INVARIANT_KEY_ALREADY_USED = "ED_INVARIANT_KEY_ALREADY_USED"; public static final String FHIRPATH_OFTYPE_IMPOSSIBLE = "FHIRPATH_OFTYPE_IMPOSSIBLE"; + public static final String FHIRPATH_AS_IMPOSSIBLE = "FHIRPATH_AS_IMPOSSIBLE"; public static final String ED_SEARCH_EXPRESSION_ERROR = "ED_SEARCH_EXPRESSION_ERROR"; public static final String SD_EXTENSION_URL_MISMATCH = "SD_EXTENSION_URL_MISMATCH"; public static final String MSG_DEPRECATED = "MSG_DEPRECATED"; @@ -1015,6 +1016,12 @@ public class I18nConstants { public static final String UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV = "UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV"; public static final String ED_CONTEXT_INVARIANT_EXPRESSION_ERROR = "ED_CONTEXT_INVARIANT_EXPRESSION_ERROR"; public static final String VALIDATION_VAL_PROFILE_SIGNPOST_OBS = "VALIDATION_VAL_PROFILE_SIGNPOST_OBS"; + public static final String FHIRPATH_INVALID_TYPE = "FHIRPATH_INVALID_TYPE"; + public static final String FHIRPATH_AS_COLLECTION = "FHIRPATH_AS_COLLECTION"; + public static final String FHIRPATH_ARITHMETIC_QTY = "FHIRPATH_ARITHMETIC_QTY"; + public static final String FHIRPATH_ARITHMETIC_UNIT = "FHIRPATH_ARITHMETIC_UNIT"; + public static final String FHIRPATH_ARITHMETIC_PLUS = "FHIRPATH_ARITHMETIC_PLUS"; + public static final String FHIRPATH_ARITHMETIC_MINUS = "FHIRPATH_ARITHMETIC_MINUS"; } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 90062f33f..bfcd99321 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -1004,7 +1004,8 @@ LIQUID_VARIABLE_ILLEGAL = Liquid Exception: The variable name ''{0}'' cannot be ED_INVARIANT_DIFF_NO_SOURCE = The invariant {0} defined in the differential must have no source, or the source must be the same as the profile FHIRPATH_COLLECTION_STATUS_OPERATION_LEFT = The left side is inherently a collection, and so the expression ''{0}'' may fail or return false if there is more than one item in the content being evaluated FHIRPATH_COLLECTION_STATUS_OPERATION_RIGHT = The right side is inherently a collection, and so this expression ''{0}'' may fail or return false if there is more than one item in the content being evaluated -FHIRPATH_OFTYPE_IMPOSSIBLE = The type specified in ofType is {1} which is not a possible candidate for the existing types ({0}) in the expression {2}. Check the paths and types to be sure this is what is intended +FHIRPATH_OFTYPE_IMPOSSIBLE = The type specified in ofType() is {1} which is not a possible candidate for the existing types ({0}) in the expression {2}. Check the paths and types to be sure this is what is intended +FHIRPATH_AS_IMPOSSIBLE = The type specified in as() is {1} which is not a possible candidate for the existing types ({0}) in the expression {2}. Check the paths and types to be sure this is what is intended ED_SEARCH_EXPRESSION_ERROR = Error in search expression ''{0}'': {1} SD_EXTENSION_URL_MISMATCH = The fixed value for the extension URL is {1} which doesn''t match the canonical URL {0} SD_EXTENSION_URL_MISSING = The value of Extension.url is not fixed to the extension URL {0} @@ -1072,4 +1073,9 @@ CDA_UNKNOWN_TEMPLATE_EXT = The CDA Template {0} / {1} is not known UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV = The types could not be determined from the extension context, so the invariant can't be validated (types = {0}) ED_CONTEXT_INVARIANT_EXPRESSION_ERROR = Error in constraint ''{0}'': {1} VALIDATION_VAL_PROFILE_SIGNPOST_OBS = Validate Observation against {1} profile because the {2} code {3} was found - \ No newline at end of file +FHIRPATH_INVALID_TYPE = The type {0} is not valid +FHIRPATH_AS_COLLECTION = Attempt to use ''as()'' on more than one item (''{0}'', ''{1}'') +FHIRPATH_ARITHMETIC_QTY = Error in date arithmetic: attempt to add a definite quantity duration time unit {0} +FHIRPATH_ARITHMETIC_UNIT = Error in date arithmetic: unrecognized time unit {0} +FHIRPATH_ARITHMETIC_PLUS = Error in date arithmetic: Unable to add type {0} to {1} +FHIRPATH_ARITHMETIC_MINUS = Error in date arithmetic: Unable to subtract type {0} to {1} 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 dc324a8d9..be6470a0a 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 @@ -448,7 +448,7 @@ public class BaseValidator implements IValidationContextResourceLoader { protected boolean ruleInv(List errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String theMessage, String invId, Object... theMessageArguments) { if (!thePass && doingErrors()) { String message = context.formatMessage(theMessage, theMessageArguments); - addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, theMessage).setInvId(invId); + addValidationMessage(errors, ruleDate, type, line, col, path, message, IssueSeverity.ERROR, invId).setInvId(invId); } return thePass; } @@ -472,7 +472,7 @@ public class BaseValidator implements IValidationContextResourceLoader { protected boolean txRule(List errors, String ruleDate, String txLink, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { if (!thePass && doingErrors()) { String message = context.formatMessage(theMessage, theMessageArguments); - ValidationMessage vm = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, message, IssueSeverity.ERROR).setMessageId(theMessage); + ValidationMessage vm = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, message, IssueSeverity.ERROR).setMessageId(idForMessage(theMessage, message)); vm.setRuleDate(ruleDate); if (checkMsgId(theMessage, vm)) { errors.add(vm.setTxLink(txLink)); @@ -481,6 +481,10 @@ public class BaseValidator implements IValidationContextResourceLoader { return thePass; } + private String idForMessage(String theMessage, String message) { + return theMessage.equals(message) ? null : theMessage; + } + /** * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails * @@ -605,18 +609,33 @@ public class BaseValidator implements IValidationContextResourceLoader { } + protected boolean warning(List errors, String ruleDate, IssueType type, int line, int col, String path, String id, boolean thePass, String msg, Object... theMessageArguments) { + if (!thePass && doingWarnings()) { + String nmsg = context.formatMessage(msg, theMessageArguments); + IssueSeverity severity = IssueSeverity.WARNING; + addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, id); + } + return thePass; + + } + protected boolean warningInv(List errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, String msg, String invId, Object... theMessageArguments) { if (!thePass && doingWarnings()) { String nmsg = context.formatMessage(msg, theMessageArguments); IssueSeverity severity = IssueSeverity.WARNING; - addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, msg).setInvId(invId); + String id = idForMessage(msg, nmsg); + addValidationMessage(errors, ruleDate, type, line, col, path, nmsg, severity, id).setMessageId(id).setInvId(invId); } return thePass; } protected boolean warning(List errors, String ruleDate, IssueType type, NodeStack stack, boolean thePass, String msg, Object... theMessageArguments) { - return warning(errors, ruleDate, type, stack.line(), stack.col(), stack.getLiteralPath(), thePass, msg, theMessageArguments); + return warning(errors, ruleDate, type, stack, null, thePass, msg, theMessageArguments); + } + + protected boolean warning(List errors, String ruleDate, IssueType type, NodeStack stack, String id, boolean thePass, String msg, Object... theMessageArguments) { + return warning(errors, ruleDate, type, stack.line(), stack.col(), stack.getLiteralPath(), id, thePass, msg, theMessageArguments); } protected boolean warningPlural(List errors, String ruleDate, IssueType type, int line, int col, String path, boolean thePass, int num, String msg, Object... theMessageArguments) { @@ -664,7 +683,7 @@ public class BaseValidator implements IValidationContextResourceLoader { protected boolean txWarning(List errors, String ruleDate, String txLink, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) { if (!thePass && doingWarnings()) { String nmsg = context.formatMessage(msg, theMessageArguments); - ValidationMessage vmsg = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, nmsg, IssueSeverity.WARNING).setTxLink(txLink).setMessageId(msg); + ValidationMessage vmsg = new ValidationMessage(Source.TerminologyEngine, type, line, col, path, nmsg, IssueSeverity.WARNING).setTxLink(txLink).setMessageId(idForMessage(msg, nmsg)); vmsg.setRuleDate(ruleDate); if (checkMsgId(msg, vmsg)) { errors.add(vmsg); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java index 09ea98441..7d67bb852 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java @@ -11,6 +11,7 @@ import org.hl7.fhir.r5.model.ExpressionNode.Kind; import org.hl7.fhir.r5.model.ExpressionNode.Operation; import org.hl7.fhir.r5.model.SearchParameter; import org.hl7.fhir.r5.utils.FHIRPathEngine; +import org.hl7.fhir.r5.utils.FHIRPathEngine.IssueMessage; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -77,10 +78,10 @@ public class SearchParameterValidator extends BaseValidator { private boolean checkExpression(List errors, NodeStack stack, String expression, List bases) { boolean ok = true; try { - List warnings = new ArrayList<>(); + List warnings = new ArrayList<>(); fpe.checkOnTypes(null, null, bases, fpe.parse(expression), warnings); - for (String s : warnings) { - warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, false, s); + for (IssueMessage m : warnings) { + warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, m.getId(), false, m.getMessage()); } } catch (Exception e) { if (debug) { 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 1d9497c1c..c87535cd0 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 @@ -37,6 +37,7 @@ import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass; import org.hl7.fhir.r5.utils.FHIRPathEngine; +import org.hl7.fhir.r5.utils.FHIRPathEngine.IssueMessage; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.Utilities; @@ -554,15 +555,20 @@ public class StructureDefinitionValidator extends BaseValidator { // we got to the root before finding anything typed types.add(elements.get(0).getNamedChildValue("path")); } - List warnings = new ArrayList<>(); + List warnings = new ArrayList<>(); ValidationContext vc = new ValidationContext(invariant); if (Utilities.existsInList(rootPath, context.getResourceNames())) { fpe.checkOnTypes(vc, rootPath, types, fpe.parse(exp), warnings); } else { - fpe.checkOnTypes(vc, "DomainResource", types, fpe.parse(exp), warnings); + StructureDefinition sd = context.fetchTypeDefinition(rootPath); + if (sd != null) { + fpe.checkOnTypes(vc, rootPath, types, fpe.parse(exp), warnings); + } else { + fpe.checkOnTypes(vc, "DomainResource", types, fpe.parse(exp), warnings); + } } - for (String s : warnings) { - warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, false, key+": "+s); + for (IssueMessage s : warnings) { + warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, s.getId(), false, key+": "+s.getMessage()); } } catch (Exception e) { if (debug) { @@ -593,11 +599,11 @@ public class StructureDefinitionValidator extends BaseValidator { hint(errors, "2023-10-31", IssueType.INFORMATIONAL, stack, false, I18nConstants.UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV, listContexts(sd)); } else try { - List warnings = new ArrayList<>(); + List warnings = new ArrayList<>(); ValidationContext vc = new ValidationContext(invariant); fpe.checkOnTypes(vc, "DomainResource", types, fpe.parse(exp), warnings); - for (String s : warnings) { - warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, false, s); + for (IssueMessage s : warnings) { + warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, s.getId(), false, s.getMessage()); } } catch (Exception e) { if (debug) { diff --git a/pom.xml b/pom.xml index ac1a0b1cf..feaeab2b7 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 32.0.1-jre 6.4.1 - 1.4.15 + 1.4.16-SNAPSHOT 2.15.2 5.9.2 1.8.2