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