Merge pull request #1484 from hapifhir/2023-11-gg-snapshot-extensions

2023 11 gg snapshot extensions
This commit is contained in:
Grahame Grieve 2023-11-10 21:59:19 +11:00 committed by GitHub
commit af07fc7fa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 306 additions and 1010 deletions

View File

@ -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);

View File

@ -218,7 +218,14 @@ public class ProfileUtilities extends TranslatingUtilities {
ALL_TYPES // allow any unknow profile
}
private static final List<String> NON_INHERITED_ED_URLS = Arrays.asList(
/**
* These extensions are stripped in inherited profiles (and may be replaced by
*/
public static final List<String> 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<String> 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<String> 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<String> 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<String> resourceNames, Set<String> baseFilenames, Set<String> 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<String> resourceNames, Set<String> baseFilenames, Set<String> 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));
}
}
}

View File

@ -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;
}
}

View File

@ -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";
}

View File

@ -62,12 +62,20 @@ public class TypeDetails {
public static final Set<String> FP_NUMBERS = new HashSet<String>(Arrays.asList(FP_Integer, FP_Decimal));
public static class ProfiledType {
@Override
public String toString() {
return uri;
}
private String uri;
private List<String> profiles; // or, not and
private List<ElementDefinitionBindingComponent> 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() {

View File

@ -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<String> typeWarnings = new ArrayList<>();
private List<IssueMessage> 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<String> typeList, ExpressionNode expr, List<String> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails checkOnTypes(Object appContext, String resourceType, List<String> typeList, ExpressionNode expr, List<IssueMessage> 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<String> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails checkOnTypes(Object appContext, String resourceType, TypeDetails types, ExpressionNode expr, List<IssueMessage> 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();

View File

@ -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());

View File

@ -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<String> warnings = new ArrayList<>();
List<IssueMessage> 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<String> warnings = new ArrayList<>();
List<IssueMessage> 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<String> warnings = new ArrayList<>();
List<IssueMessage> 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<String> types = new ArrayList<>();
List<String> warnings = new ArrayList<>();
List<IssueMessage> 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());
}
}
}

View File

@ -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<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
// 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<ValidationMessage> messages = new ArrayList<ValidationMessage>();
//
// 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;
// }
//
}

View File

@ -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;
}
}

View File

@ -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";
}

View File

@ -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
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}

View File

@ -448,7 +448,7 @@ public class BaseValidator implements IValidationContextResourceLoader {
protected boolean ruleInv(List<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> 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<ValidationMessage> 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);

View File

@ -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<ValidationMessage> errors, NodeStack stack, String expression, List<String> bases) {
boolean ok = true;
try {
List<String> warnings = new ArrayList<>();
List<IssueMessage> 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) {

View File

@ -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<String> warnings = new ArrayList<>();
List<IssueMessage> 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<String> warnings = new ArrayList<>();
List<IssueMessage> 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) {

View File

@ -20,7 +20,7 @@
<properties>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.4.15</validator_test_case_version>
<validator_test_case_version>1.4.16-SNAPSHOT</validator_test_case_version>
<jackson_version>2.15.2</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>