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