001package org.hl7.fhir.r5.terminologies.utilities;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006import java.util.Set;
007
008import org.hl7.fhir.r5.model.CodeableConcept;
009import org.hl7.fhir.r5.model.Coding;
010import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
011import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
012import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
013import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
014
015public class ValidationResult {
016  private ConceptDefinitionComponent definition;
017  private String preferredDisplay;
018  private String system;
019  private String version;
020  private IssueSeverity severity;
021  private List<String> messages = new ArrayList<>();
022  private TerminologyServiceErrorClass errorClass;
023  private String txLink;
024  private String diagnostics;
025  private List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
026  private CodeableConcept codeableConcept;
027  private Set<String> unknownSystems;
028  private boolean inactive;
029  private String status;
030  private String server;
031  
032  @Override
033  public String toString() {
034    return "ValidationResult [definition=" + definition + ", system=" + system + ", severity=" + severity + ", message=" + getMessage() + ", errorClass="
035        + errorClass + ", txLink=" + txLink + "]";
036  }
037
038  public ValidationResult(IssueSeverity severity, String message, List<OperationOutcomeIssueComponent> issues) {
039    this.severity = severity;
040    if (message != null) {
041      this.messages.add(message);
042    }
043    if (issues != null) {
044      this.issues.addAll(issues);
045    }
046  }
047
048  public ValidationResult(String system, String version, ConceptDefinitionComponent definition, String preferredDisplay) {
049    this.system = system;
050    this.version = version;
051    this.definition = definition;
052    this.preferredDisplay = preferredDisplay;
053  }
054
055  public ValidationResult(IssueSeverity severity, String message, String system, String version, ConceptDefinitionComponent definition, String preferredDisplay, List<OperationOutcomeIssueComponent>  issues) {
056    this.severity = severity;
057    if (message != null) {
058      this.messages.add(message);
059    }
060    this.system = system;
061    this.version = version;
062    this.definition = definition;
063    this.preferredDisplay = preferredDisplay;
064    if (issues != null) {
065      this.issues.addAll(issues);
066    }
067  }
068  public ValidationResult(IssueSeverity severity, List<String> messages, String system, String version, ConceptDefinitionComponent definition, String preferredDisplay, List<OperationOutcomeIssueComponent>  issues) {
069    this.severity = severity;
070    this.messages.addAll(messages);
071    this.system = system;
072    this.version = version;
073    this.definition = definition;
074    this.preferredDisplay = preferredDisplay;
075    if (issues != null) {
076      this.issues.addAll(issues);
077    }
078  }
079
080  public ValidationResult(IssueSeverity severity, String message, TerminologyServiceErrorClass errorClass, List<OperationOutcomeIssueComponent>  issues) {
081    this.severity = severity;
082    if (message != null) {
083      this.messages.add(message);
084    }
085    this.errorClass = errorClass;
086    if (issues != null) {
087      this.issues.addAll(issues);
088    }
089  }
090
091  public boolean isOk() {
092    return severity == null || severity == IssueSeverity.INFORMATION || severity == IssueSeverity.WARNING;
093  }
094
095  public String getSystem() {
096    return system;
097  }
098
099  public String getVersion() {
100    return version;
101  }
102
103  public String getDisplay() {
104    if (preferredDisplay != null) {
105      return preferredDisplay; 
106    } else {
107      return definition == null ? null : definition.getDisplay();
108    }
109  }
110
111  public void setDisplay(String display) {
112    this.preferredDisplay = display;
113  }
114
115  public void setSystem(String system) {
116    this.system = system;
117  }
118
119  public void setVersion(String version) {
120    this.version = version;
121  }
122
123  public String getCode() {
124    return definition == null ? null : definition.getCode();
125  }
126
127  public String getDefinition() {
128    return definition == null ? null : definition.getDefinition();
129  }
130
131  public void setDefinition(ConceptDefinitionComponent definition) {
132    this.definition = definition;
133  }
134
135  public ConceptDefinitionComponent asConceptDefinition() {
136    return definition;
137  }
138
139  public IssueSeverity getSeverity() {
140    return severity;
141  }
142
143  public String getMessage() {
144    if (messages.size() == 0) {
145      return null;
146    }
147    Collections.sort(messages);
148    return CommaSeparatedStringBuilder.join("; ", messages);
149  }
150
151  public String getTrimmedMessage() {
152    List<String> toTrim = new ArrayList<>();
153    for (OperationOutcomeIssueComponent iss : getIssues()) {
154      toTrim.add(iss.getDetails().getText());
155    }
156    List<String> trimmed = new ArrayList<>();
157    trimmed.addAll(messages);
158    trimmed.removeAll(toTrim);
159    if (trimmed.size() == 0) {
160      return null;
161    }       
162    Collections.sort(trimmed);
163    return CommaSeparatedStringBuilder.join("; ", trimmed);
164  }
165
166  public boolean IsNoService() {
167    return errorClass == TerminologyServiceErrorClass.NOSERVICE;
168  }
169
170  public TerminologyServiceErrorClass getErrorClass() {
171    return errorClass;
172  }
173
174  public ValidationResult setSeverity(IssueSeverity severity) {
175    this.severity = severity;
176    return this;
177  }
178
179  public ValidationResult setMessage(String message) {
180    this.messages.clear();
181    if (message != null) {
182      this.messages.add(message);
183    }
184    return this;
185  }
186  
187  public ValidationResult addMessage(String message) {
188    if (message != null) {
189      this.messages.add(message);
190    }
191    return this;
192  }
193  
194  public ValidationResult setErrorClass(TerminologyServiceErrorClass errorClass) {
195    this.errorClass = errorClass;
196    return this;
197  }
198
199  public String getTxLink() {
200    return txLink;
201  }
202
203  public ValidationResult setTxLink(String txLink) {
204    this.txLink = txLink;
205    return this;
206  }
207
208  public boolean hasMessage() {
209    return !messages.isEmpty();
210  }
211
212  public String getDiagnostics() {
213    return diagnostics;
214  }
215
216  public void setDiagnostics(String diagnostics) {
217    this.diagnostics = diagnostics;
218  }
219
220  public Coding asCoding() {
221    if (isOk() && definition != null && definition.getCode() != null) {
222      return new Coding(system, definition.getCode(), definition.getDisplay());
223    } else {
224      return null;
225    }
226  }
227
228  public List<OperationOutcomeIssueComponent> getIssues() {
229    return issues;
230  }
231
232  public ValidationResult addCodeableConcept(CodeableConcept vcc) {
233    if (!vcc.isEmpty()) {
234      codeableConcept = vcc;
235    }
236    return this;
237  }
238
239  public CodeableConcept getCodeableConcept() {
240    return codeableConcept;
241  }
242
243  public Set<String> getUnknownSystems() {
244    return unknownSystems;
245  }
246
247  public ValidationResult setUnknownSystems(Set<String> unknownSystems) {
248    this.unknownSystems = unknownSystems;
249    return this;
250  }
251
252  public String unknownSystems() {
253    if (unknownSystems == null) {
254      return null;
255    }
256    if (unknownSystems.size() == 1) {
257      return unknownSystems.iterator().next();        
258    } else {
259      return String.join(",", unknownSystems);
260    }
261  }
262
263  public void setIssues(List<OperationOutcomeIssueComponent> issues) {
264    if (this.issues != null) {
265      issues.addAll(this.issues);
266    }
267    this.issues = issues;
268    
269  }
270
271  public void trimPath(String prefix) {
272    if (issues != null) {
273      for (OperationOutcomeIssueComponent iss : issues) {
274        for (int i = iss.getLocation().size() -1; i >= 0; i--) {
275          var s = iss.getLocation().get(i).primitiveValue();
276          if (prefix.equals(s)) {
277            iss.getLocation().remove(i);
278          } else if (s.startsWith(prefix+".")) {
279            iss.getLocation().get(i).setValueAsString(s.substring(prefix.length()+1));                
280          }            
281        }
282        for (int i = iss.getExpression().size() -1; i >= 0; i--) {
283          var s = iss.getExpression().get(i).primitiveValue();
284          if (prefix.equals(s)) {
285            iss.getExpression().remove(i);
286          } else if (s.startsWith(prefix+".")) {
287            iss.getExpression().get(i).setValueAsString(s.substring(prefix.length()+1));                
288          }            
289        }
290      }
291    }      
292    
293  }
294
295  public boolean isInactive() {
296    return inactive;
297  }
298
299  public String getStatus() {
300    return status;
301  }
302
303  public ValidationResult setStatus(boolean inactive, String status) {
304    this.inactive = inactive;
305    if (!"inactive".equals(status)) {
306      this.status = status;
307    }
308    return this;
309  }
310
311  public boolean messageIsInIssues() {
312    // the message is sometimes a summary of the issues that are already tracked. 
313    // this returns true in that case, so that duplication of messages is suppressed
314    
315    for (String s : messages) {
316      boolean found = false;
317      for (OperationOutcomeIssueComponent iss : issues) {
318        if (iss.getSeverity().ordinal() <= getSeverity().ordinal() && s.equals(iss.getDetails().getText())) {
319          found = true;
320        }
321      }
322      if (!found) {
323        return false;
324      }
325    }
326    return true;
327  }
328
329  public String getServer() {
330    return server;
331  }
332
333  public void setServer(String server) {
334    this.server = server;
335  }
336
337}