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