001package org.hl7.fhir.r5.terminologies.utilities; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import org.hl7.fhir.r5.context.IWorkerContext; 007import org.hl7.fhir.r5.model.BooleanType; 008import org.hl7.fhir.r5.model.CanonicalResource; 009import org.hl7.fhir.r5.model.DataType; 010import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; 011import org.hl7.fhir.r5.model.OperationOutcome.IssueType; 012import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent; 013import org.hl7.fhir.r5.model.Extension; 014import org.hl7.fhir.r5.model.Parameters; 015import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent; 016import org.hl7.fhir.r5.model.UriType; 017import org.hl7.fhir.r5.model.ValueSet; 018import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; 019import org.hl7.fhir.r5.utils.ToolingExtensions; 020import org.hl7.fhir.utilities.StandardsStatus; 021import org.hl7.fhir.utilities.Utilities; 022import org.hl7.fhir.utilities.i18n.I18nConstants; 023import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 024 025public class ValueSetProcessBase { 026 027 protected IWorkerContext context; 028 protected TerminologyOperationContext opContext; 029 protected List<String> requiredSupplements = new ArrayList<>(); 030 031 protected ValueSetProcessBase(IWorkerContext context, TerminologyOperationContext opContext) { 032 super(); 033 this.context = context; 034 this.opContext = opContext; 035 } 036 public static class AlternateCodesProcessingRules { 037 private boolean all; 038 private List<String> uses = new ArrayList<>(); 039 040 public AlternateCodesProcessingRules(boolean b) { 041 all = b; 042 } 043 044 private void seeParameter(DataType value) { 045 if (value != null) { 046 if (value instanceof BooleanType) { 047 all = ((BooleanType) value).booleanValue(); 048 uses.clear(); 049 } else if (value.isPrimitive()) { 050 String s = value.primitiveValue(); 051 if (!Utilities.noString(s)) { 052 uses.add(s); 053 } 054 } 055 } 056 } 057 058 public void seeParameters(Parameters pp) { 059 for (ParametersParameterComponent p : pp.getParameter()) { 060 String name = p.getName(); 061 if ("includeAlternateCodes".equals(name)) { 062 DataType value = p.getValue(); 063 seeParameter(value); 064 } 065 } 066 } 067 068 public void seeValueSet(ValueSet vs) { 069 if (vs != null) { 070 for (Extension ext : vs.getCompose().getExtension()) { 071 if ("http://hl7.org/fhir/tools/StructureDefinion/valueset-expansion-param".equals(ext.getUrl())) { 072 String name = ext.getExtensionString("name"); 073 Extension value = ext.getExtensionByUrl("value"); 074 if ("includeAlternateCodes".equals(name) && value != null && value.hasValue()) { 075 seeParameter(value.getValue()); 076 } 077 } 078 } 079 } 080 } 081 082 public boolean passes(List<Extension> extensions) { 083 if (all) { 084 return true; 085 } 086 087 for (Extension ext : extensions) { 088 if (ToolingExtensions.EXT_CS_ALTERNATE_USE.equals(ext.getUrl())) { 089 if (ext.hasValueCoding() && Utilities.existsInList(ext.getValueCoding().getCode(), uses)) { 090 return true; 091 } 092 } 093 } 094 return false; 095 } 096 } 097 098 099 protected List<OperationOutcomeIssueComponent> makeIssue(IssueSeverity level, IssueType type, String location, String message) { 100 OperationOutcomeIssueComponent result = new OperationOutcomeIssueComponent(); 101 switch (level) { 102 case ERROR: 103 result.setSeverity(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR); 104 break; 105 case FATAL: 106 result.setSeverity(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.FATAL); 107 break; 108 case INFORMATION: 109 result.setSeverity(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION); 110 break; 111 case WARNING: 112 result.setSeverity(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING); 113 break; 114 } 115 result.setCode(type); 116 if (location != null) { 117 result.addLocation(location); 118 } 119 result.getDetails().setText(message); 120 ArrayList<OperationOutcomeIssueComponent> list = new ArrayList<>(); 121 list.add(result); 122 return list; 123 } 124 125 public void checkCanonical(List<OperationOutcomeIssueComponent> issues, String path, CanonicalResource resource, CanonicalResource source) { 126 if (resource != null) { 127 StandardsStatus standardsStatus = ToolingExtensions.getStandardsStatus(resource); 128 if (standardsStatus == StandardsStatus.DEPRECATED) { 129 addToIssues(issues, makeStatusIssue(path, "deprecated", I18nConstants.MSG_DEPRECATED, resource)); 130 } else if (standardsStatus == StandardsStatus.WITHDRAWN) { 131 addToIssues(issues, makeStatusIssue(path, "withdrawn", I18nConstants.MSG_WITHDRAWN, resource)); 132 } else if (resource.getStatus() == PublicationStatus.RETIRED) { 133 addToIssues(issues, makeStatusIssue(path, "retired", I18nConstants.MSG_RETIRED, resource)); 134 } else if (source != null) { 135 if (resource.getExperimental() && !source.getExperimental()) { 136 addToIssues(issues, makeStatusIssue(path, "experimental", I18nConstants.MSG_EXPERIMENTAL, resource)); 137 } else if ((resource.getStatus() == PublicationStatus.DRAFT || standardsStatus == StandardsStatus.DRAFT) 138 && !(source.getStatus() == PublicationStatus.DRAFT || ToolingExtensions.getStandardsStatus(source) == StandardsStatus.DRAFT)) { 139 addToIssues(issues, makeStatusIssue(path, "draft", I18nConstants.MSG_DRAFT, resource)); 140 } 141 } 142 } 143 } 144 145 private List<OperationOutcomeIssueComponent> makeStatusIssue(String path, String id, String msg, CanonicalResource resource) { 146 List<OperationOutcomeIssueComponent> iss = makeIssue(IssueSeverity.INFORMATION, IssueType.BUSINESSRULE, path, context.formatMessage(msg, resource.getVersionedUrl())); 147 148 // this is a testing hack - see TerminologyServiceTests 149 iss.get(0).setUserData("status-msg-name", "warning-"+id); 150 iss.get(0).setUserData("status-msg-value", new UriType(resource.getVersionedUrl())); 151 152 return iss; 153 } 154 155 private void addToIssues(List<OperationOutcomeIssueComponent> issues, List<OperationOutcomeIssueComponent> toAdd) { 156 for (OperationOutcomeIssueComponent t : toAdd) { 157 boolean found = false; 158 for (OperationOutcomeIssueComponent i : issues) { 159 if (i.getSeverity() == t.getSeverity() && i.getCode() == t.getCode() && i.getDetails().getText().equals(t.getDetails().getText())) { // ignore location 160 found = true; 161 } 162 } 163 if (!found) { 164 issues.add(t); 165 } 166 } 167 } 168 169 public void checkCanonical(ValueSetExpansionComponent params, CanonicalResource resource, ValueSet source) { 170 if (resource != null) { 171 StandardsStatus standardsStatus = ToolingExtensions.getStandardsStatus(resource); 172 if (standardsStatus == StandardsStatus.DEPRECATED) { 173 if (!params.hasParameterValue("warning-deprecated", resource.getVersionedUrl())) { 174 params.addParameter("warning-deprecated", new UriType(resource.getVersionedUrl())); 175 } 176 } else if (standardsStatus == StandardsStatus.WITHDRAWN) { 177 if (!params.hasParameterValue("warning-withdrawn", resource.getVersionedUrl())) { 178 params.addParameter("warning-withdrawn", new UriType(resource.getVersionedUrl())); 179 } 180 } else if (resource.getStatus() == PublicationStatus.RETIRED) { 181 if (!params.hasParameterValue("warning-retired", resource.getVersionedUrl())) { 182 params.addParameter("warning-retired", new UriType(resource.getVersionedUrl())); 183 } 184 } else if (resource.getExperimental() && !source.getExperimental()) { 185 if (!params.hasParameterValue("warning-experimental", resource.getVersionedUrl())) { 186 params.addParameter("warning-experimental", new UriType(resource.getVersionedUrl())); 187 } 188 } else if ((resource.getStatus() == PublicationStatus.DRAFT || standardsStatus == StandardsStatus.DRAFT) 189 && !(source.getStatus() == PublicationStatus.DRAFT || ToolingExtensions.getStandardsStatus(source) == StandardsStatus.DRAFT)) { 190 if (!params.hasParameterValue("warning-draft", resource.getVersionedUrl())) { 191 params.addParameter("warning-draft", new UriType(resource.getVersionedUrl())); 192 } 193 } 194 } 195 } 196 197 198 protected AlternateCodesProcessingRules altCodeParams = new AlternateCodesProcessingRules(false); 199 protected AlternateCodesProcessingRules allAltCodes = new AlternateCodesProcessingRules(true); 200}