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}