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}