001package org.hl7.fhir.utilities.validation; 002 003import java.text.ParseException; 004import java.text.SimpleDateFormat; 005 006/* 007 Copyright (c) 2011+, HL7, Inc. 008 All rights reserved. 009 010 Redistribution and use in source and binary forms, with or without modification, 011 are permitted provided that the following conditions are met: 012 013 * Redistributions of source code must retain the above copyright notice, this 014 list of conditions and the following disclaimer. 015 * Redistributions in binary form must reproduce the above copyright notice, 016 this list of conditions and the following disclaimer in the documentation 017 and/or other materials provided with the distribution. 018 * Neither the name of HL7 nor the names of its contributors may be used to 019 endorse or promote products derived from this software without specific 020 prior written permission. 021 022 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 023 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 024 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 025 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 026 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 027 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 028 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 029 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 030 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 031 POSSIBILITY OF SUCH DAMAGE. 032 033 */ 034 035 036 037/* 038 Copyright (c) 2011+, HL7, Inc 039 All rights reserved. 040 041 Redistribution and use in source and binary forms, with or without modification, 042 are permitted provided that the following conditions are met: 043 044 * Redistributions of source code must retain the above copyright notice, this 045 list of conditions and the following disclaimer. 046 * Redistributions in binary form must reproduce the above copyright notice, 047 this list of conditions and the following disclaimer in the documentation 048 and/or other materials provided with the distribution. 049 * Neither the name of HL7 nor the names of its contributors may be used to 050 endorse or promote products derived from this software without specific 051 prior written permission. 052 053 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 054 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 055 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 056 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 057 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 058 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 059 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 060 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 061 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 062 POSSIBILITY OF SUCH DAMAGE. 063 064 */ 065 066import java.util.Comparator; 067import java.util.Date; 068import java.util.EnumMap; 069 070import org.apache.commons.lang3.builder.ToStringBuilder; 071import org.apache.commons.lang3.builder.ToStringStyle; 072import org.hl7.fhir.exceptions.FHIRException; 073import org.hl7.fhir.utilities.Utilities; 074 075public class ValidationMessage implements Comparator<ValidationMessage>, Comparable<ValidationMessage> 076{ 077 public enum Source { 078 ExampleValidator, 079 ProfileValidator, 080 ResourceValidator, 081 InstanceValidator, 082 Template, 083 Schema, 084 Schematron, 085 Publisher, 086 LinkChecker, 087 Ontology, 088 ProfileComparer, 089 TerminologyEngine, 090 QuestionnaireResponseValidator, 091 IPAValidator 092 } 093 094 public enum IssueSeverity { 095 /** 096 * The issue caused the action to fail, and no further checking could be performed. 097 */ 098 FATAL, 099 /** 100 * The issue is sufficiently important to cause the action to fail. 101 */ 102 ERROR, 103 /** 104 * The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired. 105 */ 106 WARNING, 107 /** 108 * The issue has no relation to the degree of success of the action. 109 */ 110 INFORMATION, 111 /** 112 * added to help the parsers with the generic types 113 */ 114 NULL; 115 public static IssueSeverity fromCode(String codeString) throws FHIRException { 116 if (codeString == null || "".equals(codeString)) 117 return null; 118 if ("fatal".equals(codeString)) 119 return FATAL; 120 if ("error".equals(codeString)) 121 return ERROR; 122 if ("warning".equals(codeString)) 123 return WARNING; 124 if ("information".equals(codeString)) 125 return INFORMATION; 126 else 127 throw new FHIRException("Unknown IssueSeverity code '"+codeString+"'"); 128 } 129 public String toCode() { 130 switch (this) { 131 case FATAL: return "fatal"; 132 case ERROR: return "error"; 133 case WARNING: return "warning"; 134 case INFORMATION: return "information"; 135 case NULL: return null; 136 default: return "?"; 137 } 138 } 139 public String getSystem() { 140 switch (this) { 141 case FATAL: return "http://hl7.org/fhir/issue-severity"; 142 case ERROR: return "http://hl7.org/fhir/issue-severity"; 143 case WARNING: return "http://hl7.org/fhir/issue-severity"; 144 case INFORMATION: return "http://hl7.org/fhir/issue-severity"; 145 case NULL: return null; 146 default: return "?"; 147 } 148 } 149 public String getDefinition() { 150 switch (this) { 151 case FATAL: return "The issue caused the action to fail, and no further checking could be performed."; 152 case ERROR: return "The issue is sufficiently important to cause the action to fail."; 153 case WARNING: return "The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired."; 154 case INFORMATION: return "The issue has no relation to the degree of success of the action."; 155 case NULL: return null; 156 default: return "?"; 157 } 158 } 159 public String getDisplay() { 160 switch (this) { 161 case FATAL: return "Fatal"; 162 case ERROR: return "Error"; 163 case WARNING: return "Warning"; 164 case INFORMATION: return "Information"; 165 case NULL: return null; 166 default: return "?"; 167 } 168 } 169 public boolean isError() { 170 return this == FATAL || this == ERROR; 171 } 172 public boolean isHint() { 173 return this == INFORMATION; 174 } 175 } 176 177 public enum IssueType { 178 /** 179 * Content invalid against the specification or a profile. 180 */ 181 INVALID, 182 DELETED, 183 /** 184 * A structural issue in the content such as wrong namespace, or unable to parse the content completely, or invalid json syntax. 185 */ 186 STRUCTURE, 187 /** 188 * A required element is missing. 189 */ 190 REQUIRED, 191 /** 192 * An element value is invalid. 193 */ 194 VALUE, 195 /** 196 * A content validation rule failed - e.g. a schematron rule. 197 */ 198 INVARIANT, 199 /** 200 * An authentication/authorization/permissions issue of some kind. 201 */ 202 SECURITY, 203 /** 204 * The client needs to initiate an authentication process. 205 */ 206 LOGIN, 207 /** 208 * The user or system was not able to be authenticated (either there is no process, or the proferred token is unacceptable). 209 */ 210 MULTIPLEMATCHES, 211 UNKNOWN, 212 /** 213 * User session expired; a login may be required. 214 */ 215 EXPIRED, 216 /** 217 * The user does not have the rights to perform this action. 218 */ 219 FORBIDDEN, 220 /** 221 * Some information was not or may not have been returned due to business rules, consent or privacy rules, or access permission constraints. This information may be accessible through alternate processes. 222 */ 223 SUPPRESSED, 224 /** 225 * Processing issues. These are expected to be final e.g. there is no point resubmitting the same content unchanged. 226 */ 227 PROCESSING, 228 /** 229 * The resource or profile is not supported. 230 */ 231 NOTSUPPORTED, 232 /** 233 * An attempt was made to create a duplicate record. 234 */ 235 DUPLICATE, 236 /** 237 * The reference provided was not found. In a pure RESTful environment, this would be an HTTP 404 error, but this code may be used where the content is not found further into the application architecture. 238 */ 239 NOTFOUND, 240 /** 241 * Provided content is too long (typically, this is a denial of service protection type of error). 242 */ 243 TOOLONG, 244 /** 245 * The code or system could not be understood, or it was not valid in the context of a particular ValueSet.code. 246 */ 247 CODEINVALID, 248 /** 249 * An extension was found that was not acceptable, could not be resolved, or a modifierExtension was not recognized. 250 */ 251 EXTENSION, 252 /** 253 * The operation was stopped to protect server resources; e.g. a request for a value set expansion on all of SNOMED CT. 254 */ 255 TOOCOSTLY, 256 /** 257 * The content/operation failed to pass some business rule, and so could not proceed. 258 */ 259 BUSINESSRULE, 260 /** 261 * Content could not be accepted because of an edit conflict (i.e. version aware updates) (In a pure RESTful environment, this would be an HTTP 404 error, but this code may be used where the conflict is discovered further into the application architecture.) 262 */ 263 CONFLICT, 264 /** 265 * Not all data sources typically accessed could be reached, or responded in time, so the returned information may not be complete. 266 */ 267 INCOMPLETE, 268 /** 269 * Transient processing issues. The system receiving the error may be able to resubmit the same content once an underlying issue is resolved. 270 */ 271 TRANSIENT, 272 /** 273 * A resource/record locking failure (usually in an underlying database). 274 */ 275 LOCKERROR, 276 /** 277 * The persistent store is unavailable; e.g. the database is down for maintenance or similar action. 278 */ 279 NOSTORE, 280 /** 281 * An unexpected internal error has occurred. 282 */ 283 EXCEPTION, 284 /** 285 * An internal timeout has occurred. 286 */ 287 TIMEOUT, 288 /** 289 * The system is not prepared to handle this request due to load management. 290 */ 291 THROTTLED, 292 /** 293 * A message unrelated to the processing success of the completed operation (examples of the latter include things like reminders of password expiry, system maintenance times, etc.). 294 */ 295 INFORMATIONAL, 296 /** 297 * added to help the parsers with the generic types 298 */ 299 NULL; 300 public static IssueType fromCode(String codeString) throws FHIRException { 301 if (codeString == null || "".equals(codeString)) 302 return null; 303 if ("invalid".equals(codeString)) 304 return INVALID; 305 if ("structure".equals(codeString)) 306 return STRUCTURE; 307 if ("required".equals(codeString)) 308 return REQUIRED; 309 if ("value".equals(codeString)) 310 return VALUE; 311 if ("invariant".equals(codeString)) 312 return INVARIANT; 313 if ("security".equals(codeString)) 314 return SECURITY; 315 if ("login".equals(codeString)) 316 return LOGIN; 317 if ("unknown".equals(codeString)) 318 return UNKNOWN; 319 if ("expired".equals(codeString)) 320 return EXPIRED; 321 if ("forbidden".equals(codeString)) 322 return FORBIDDEN; 323 if ("suppressed".equals(codeString)) 324 return SUPPRESSED; 325 if ("processing".equals(codeString)) 326 return PROCESSING; 327 if ("not-supported".equals(codeString)) 328 return NOTSUPPORTED; 329 if ("duplicate".equals(codeString)) 330 return DUPLICATE; 331 if ("not-found".equals(codeString)) 332 return NOTFOUND; 333 if ("too-long".equals(codeString)) 334 return TOOLONG; 335 if ("code-invalid".equals(codeString)) 336 return CODEINVALID; 337 if ("extension".equals(codeString)) 338 return EXTENSION; 339 if ("too-costly".equals(codeString)) 340 return TOOCOSTLY; 341 if ("business-rule".equals(codeString)) 342 return BUSINESSRULE; 343 if ("conflict".equals(codeString)) 344 return CONFLICT; 345 if ("incomplete".equals(codeString)) 346 return INCOMPLETE; 347 if ("transient".equals(codeString)) 348 return TRANSIENT; 349 if ("lock-error".equals(codeString)) 350 return LOCKERROR; 351 if ("no-store".equals(codeString)) 352 return NOSTORE; 353 if ("exception".equals(codeString)) 354 return EXCEPTION; 355 if ("timeout".equals(codeString)) 356 return TIMEOUT; 357 if ("throttled".equals(codeString)) 358 return THROTTLED; 359 if ("informational".equals(codeString)) 360 return INFORMATIONAL; 361 else 362 throw new FHIRException("Unknown IssueType code '"+codeString+"'"); 363 } 364 public String toCode() { 365 switch (this) { 366 case INVALID: return "invalid"; 367 case STRUCTURE: return "structure"; 368 case REQUIRED: return "required"; 369 case VALUE: return "value"; 370 case INVARIANT: return "invariant"; 371 case SECURITY: return "security"; 372 case LOGIN: return "login"; 373 case UNKNOWN: return "unknown"; 374 case EXPIRED: return "expired"; 375 case FORBIDDEN: return "forbidden"; 376 case SUPPRESSED: return "suppressed"; 377 case PROCESSING: return "processing"; 378 case NOTSUPPORTED: return "not-supported"; 379 case DUPLICATE: return "duplicate"; 380 case NOTFOUND: return "not-found"; 381 case TOOLONG: return "too-long"; 382 case CODEINVALID: return "code-invalid"; 383 case EXTENSION: return "extension"; 384 case TOOCOSTLY: return "too-costly"; 385 case BUSINESSRULE: return "business-rule"; 386 case CONFLICT: return "conflict"; 387 case INCOMPLETE: return "incomplete"; 388 case TRANSIENT: return "transient"; 389 case LOCKERROR: return "lock-error"; 390 case NOSTORE: return "no-store"; 391 case EXCEPTION: return "exception"; 392 case TIMEOUT: return "timeout"; 393 case THROTTLED: return "throttled"; 394 case INFORMATIONAL: return "informational"; 395 case NULL: return null; 396 default: return "?"; 397 } 398 } 399 public String getSystem() { 400 switch (this) { 401 case INVALID: return "http://hl7.org/fhir/issue-type"; 402 case STRUCTURE: return "http://hl7.org/fhir/issue-type"; 403 case REQUIRED: return "http://hl7.org/fhir/issue-type"; 404 case VALUE: return "http://hl7.org/fhir/issue-type"; 405 case INVARIANT: return "http://hl7.org/fhir/issue-type"; 406 case SECURITY: return "http://hl7.org/fhir/issue-type"; 407 case LOGIN: return "http://hl7.org/fhir/issue-type"; 408 case UNKNOWN: return "http://hl7.org/fhir/issue-type"; 409 case EXPIRED: return "http://hl7.org/fhir/issue-type"; 410 case FORBIDDEN: return "http://hl7.org/fhir/issue-type"; 411 case SUPPRESSED: return "http://hl7.org/fhir/issue-type"; 412 case PROCESSING: return "http://hl7.org/fhir/issue-type"; 413 case NOTSUPPORTED: return "http://hl7.org/fhir/issue-type"; 414 case DUPLICATE: return "http://hl7.org/fhir/issue-type"; 415 case NOTFOUND: return "http://hl7.org/fhir/issue-type"; 416 case TOOLONG: return "http://hl7.org/fhir/issue-type"; 417 case CODEINVALID: return "http://hl7.org/fhir/issue-type"; 418 case EXTENSION: return "http://hl7.org/fhir/issue-type"; 419 case TOOCOSTLY: return "http://hl7.org/fhir/issue-type"; 420 case BUSINESSRULE: return "http://hl7.org/fhir/issue-type"; 421 case CONFLICT: return "http://hl7.org/fhir/issue-type"; 422 case INCOMPLETE: return "http://hl7.org/fhir/issue-type"; 423 case TRANSIENT: return "http://hl7.org/fhir/issue-type"; 424 case LOCKERROR: return "http://hl7.org/fhir/issue-type"; 425 case NOSTORE: return "http://hl7.org/fhir/issue-type"; 426 case EXCEPTION: return "http://hl7.org/fhir/issue-type"; 427 case TIMEOUT: return "http://hl7.org/fhir/issue-type"; 428 case THROTTLED: return "http://hl7.org/fhir/issue-type"; 429 case INFORMATIONAL: return "http://hl7.org/fhir/issue-type"; 430 case NULL: return null; 431 default: return "?"; 432 } 433 } 434 public String getDefinition() { 435 switch (this) { 436 case INVALID: return "Content invalid against the specification or a profile."; 437 case STRUCTURE: return "A structural issue in the content such as wrong namespace, or unable to parse the content completely, or invalid json syntax."; 438 case REQUIRED: return "A required element is missing."; 439 case VALUE: return "An element value is invalid."; 440 case INVARIANT: return "A content validation rule failed - e.g. a schematron rule."; 441 case SECURITY: return "An authentication/authorization/permissions issue of some kind."; 442 case LOGIN: return "The client needs to initiate an authentication process."; 443 case UNKNOWN: return "The user or system was not able to be authenticated (either there is no process, or the proferred token is unacceptable)."; 444 case EXPIRED: return "User session expired; a login may be required."; 445 case FORBIDDEN: return "The user does not have the rights to perform this action."; 446 case SUPPRESSED: return "Some information was not or may not have been returned due to business rules, consent or privacy rules, or access permission constraints. This information may be accessible through alternate processes."; 447 case PROCESSING: return "Processing issues. These are expected to be final e.g. there is no point resubmitting the same content unchanged."; 448 case NOTSUPPORTED: return "The resource or profile is not supported."; 449 case DUPLICATE: return "An attempt was made to create a duplicate record."; 450 case NOTFOUND: return "The reference provided was not found. In a pure RESTful environment, this would be an HTTP 404 error, but this code may be used where the content is not found further into the application architecture."; 451 case TOOLONG: return "Provided content is too long (typically, this is a denial of service protection type of error)."; 452 case CODEINVALID: return "The code or system could not be understood, or it was not valid in the context of a particular ValueSet.code."; 453 case EXTENSION: return "An extension was found that was not acceptable, could not be resolved, or a modifierExtension was not recognized."; 454 case TOOCOSTLY: return "The operation was stopped to protect server resources; e.g. a request for a value set expansion on all of SNOMED CT."; 455 case BUSINESSRULE: return "The content/operation failed to pass some business rule, and so could not proceed."; 456 case CONFLICT: return "Content could not be accepted because of an edit conflict (i.e. version aware updates) (In a pure RESTful environment, this would be an HTTP 404 error, but this code may be used where the conflict is discovered further into the application architecture.)"; 457 case INCOMPLETE: return "Not all data sources typically accessed could be reached, or responded in time, so the returned information may not be complete."; 458 case TRANSIENT: return "Transient processing issues. The system receiving the error may be able to resubmit the same content once an underlying issue is resolved."; 459 case LOCKERROR: return "A resource/record locking failure (usually in an underlying database)."; 460 case NOSTORE: return "The persistent store is unavailable; e.g. the database is down for maintenance or similar action."; 461 case EXCEPTION: return "An unexpected internal error has occurred."; 462 case TIMEOUT: return "An internal timeout has occurred."; 463 case THROTTLED: return "The system is not prepared to handle this request due to load management."; 464 case INFORMATIONAL: return "A message unrelated to the processing success of the completed operation (examples of the latter include things like reminders of password expiry, system maintenance times, etc.)."; 465 case NULL: return null; 466 default: return "?"; 467 } 468 } 469 public String getDisplay() { 470 switch (this) { 471 case INVALID: return "Invalid Content"; 472 case STRUCTURE: return "Structural Issue"; 473 case REQUIRED: return "Required element missing"; 474 case VALUE: return "Element value invalid"; 475 case INVARIANT: return "Validation rule failed"; 476 case SECURITY: return "Security Problem"; 477 case LOGIN: return "Login Required"; 478 case UNKNOWN: return "Unknown User"; 479 case EXPIRED: return "Session Expired"; 480 case FORBIDDEN: return "Forbidden"; 481 case SUPPRESSED: return "Information Suppressed"; 482 case PROCESSING: return "Processing Failure"; 483 case NOTSUPPORTED: return "Content not supported"; 484 case DUPLICATE: return "Duplicate"; 485 case NOTFOUND: return "Not Found"; 486 case TOOLONG: return "Content Too Long"; 487 case CODEINVALID: return "Invalid Code"; 488 case EXTENSION: return "Unacceptable Extension"; 489 case TOOCOSTLY: return "Operation Too Costly"; 490 case BUSINESSRULE: return "Business Rule Violation"; 491 case CONFLICT: return "Edit Version Conflict"; 492 case INCOMPLETE: return "Incomplete Results"; 493 case TRANSIENT: return "Transient Issue"; 494 case LOCKERROR: return "Lock Error"; 495 case NOSTORE: return "No Store Available"; 496 case EXCEPTION: return "Exception"; 497 case TIMEOUT: return "Timeout"; 498 case THROTTLED: return "Throttled"; 499 case INFORMATIONAL: return "Informational Note"; 500 case NULL: return null; 501 default: return "?"; 502 } 503 } 504 } 505 506 507 private Source source; 508 private int line; 509 private int col; 510 private String location; // fhirPath 511 private String message; 512 private String messageId; // source, for grouping 513 private IssueType type; 514 private IssueSeverity level; 515 private String html; 516 private String locationLink; 517 private String txLink; 518 public String sliceHtml; 519 public String[] sliceText; 520 private boolean slicingHint; 521 private boolean signpost; 522 private boolean criticalSignpost; 523 private Date ruleDate; 524 public static final String NO_RULE_DATE = null; 525 526 527 /** 528 * Constructor 529 */ 530 public ValidationMessage() { 531 // nothing 532 } 533 534 public ValidationMessage(Source source, IssueType type, String path, String message, IssueSeverity level) { 535 this(); 536 this.line = -1; 537 this.col = -1; 538 this.location = path; 539 if (message == null) 540 throw new Error("message is null"); 541 this.message = message; 542 this.html = Utilities.escapeXml(message); 543 this.level = level; 544 this.source = source; 545 this.type = type; 546 if (level == IssueSeverity.NULL) 547 determineLevel(path); 548 if (type == null) 549 throw new Error("A type must be provided"); 550 } 551 552 public ValidationMessage(Source source, IssueType type, int line, int col, String path, String message, IssueSeverity level) { 553 this(); 554 this.line = line; 555 this.col = col; 556 this.location = path; 557 this.message = message; 558 this.html = Utilities.escapeXml(message); 559 this.level = level; 560 this.source = source; 561 this.type = type; 562 if (level == IssueSeverity.NULL) 563 determineLevel(path); 564 if (type == null) 565 throw new Error("A type must be provided"); 566 } 567 568 public ValidationMessage(Source source, IssueType type, String path, String message, String html, IssueSeverity level) { 569 this(); 570 this.line = -1; 571 this.col = -1; 572 this.location = path; 573 if (message == null) 574 throw new Error("message is null"); 575 this.message = message; 576 this.html = html; 577 this.level = level; 578 this.source = source; 579 this.type = type; 580 if (level == IssueSeverity.NULL) 581 determineLevel(path); 582 if (type == null) 583 throw new Error("A type must be provided"); 584 } 585 586 public ValidationMessage(Source source, IssueType type, int line, int col, String path, String message, String html, IssueSeverity level) { 587 this(); 588 this.line = line; 589 this.col = col; 590 this.location = path; 591 if (message == null) 592 throw new Error("message is null"); 593 this.message = message; 594 this.html = html; 595 this.level = level; 596 this.source = source; 597 this.type = type; 598 if (level == IssueSeverity.NULL) 599 determineLevel(path); 600 if (type == null) 601 throw new Error("A type must be provided"); 602 } 603 604 private IssueSeverity determineLevel(String path) { 605 if (isGrandfathered(path)) 606 return IssueSeverity.WARNING; 607 else 608 return IssueSeverity.ERROR; 609 } 610 611 private boolean isGrandfathered(String path) { 612 if (path.startsWith("xds-documentmanifest.")) 613 return true; 614 if (path.startsWith("observation-device-metric-devicemetricobservation.")) 615 return true; 616 if (path.startsWith("medicationadministration-immunization-vaccine.")) 617 return true; 618 if (path.startsWith("elementdefinition-de-dataelement.")) 619 return true; 620 if (path.startsWith("dataelement-sdc-sdcelement.")) 621 return true; 622 if (path.startsWith("questionnaireresponse-sdc-structureddatacaptureanswers.")) 623 return true; 624 if (path.startsWith("valueset-sdc-structureddatacapturevalueset.")) 625 return true; 626 if (path.startsWith("dataelement-sdc-de-sdcelement.")) 627 return true; 628 if (path.startsWith("do-uslab-uslabdo.")) 629 return true; 630 if (path.startsWith(".")) 631 return true; 632 if (path.startsWith(".")) 633 return true; 634 if (path.startsWith(".")) 635 return true; 636 if (path.startsWith(".")) 637 return true; 638 639 return false; 640 } 641 642 public String getMessage() { 643 return message; 644 } 645 public ValidationMessage setMessage(String message) { 646 this.message = message; 647 return this; 648 } 649 650 public IssueSeverity getLevel() { 651 return level; 652 } 653 public ValidationMessage setLevel(IssueSeverity level) { 654 this.level = level; 655 return this; 656 } 657 658 public Source getSource() { 659 return source; 660 } 661 public ValidationMessage setSource(Source source) { 662 this.source = source; 663 return this; 664 } 665 666 public int getLine() { 667 return line; 668 } 669 670 public void setLine(int theLine) { 671 line = theLine; 672 } 673 674 public int getCol() { 675 return col; 676 } 677 678 public void setCol(int theCol) { 679 col = theCol; 680 } 681 682 public String getLocation() { 683 return location; 684 } 685 public ValidationMessage setLocation(String location) { 686 this.location = location; 687 return this; 688 } 689 690 public IssueType getType() { 691 return type; 692 } 693 694 public ValidationMessage setType(IssueType type) { 695 this.type = type; 696 return this; 697 } 698 699 public String summary() { 700 return level.toString()+" @ "+location+(line>= 0 && col >= 0 ? " (line "+Integer.toString(line)+", col"+Integer.toString(col)+"): " : ": ") +message +(source != null ? " (src = "+source+")" : ""); 701 } 702 703 704 public String toXML() { 705 return "<message source=\"" + source + "\" line=\"" + line + "\" col=\"" + col + "\" location=\"" + Utilities.escapeXml(location) + "\" type=\"" + type + "\" level=\"" + level + "\" display=\"" + Utilities.escapeXml(getDisplay()) + "\" ><plain>" + Utilities.escapeXml(message) + "</plain><html>" + html + "</html></message>"; 706 } 707 708 public String getHtml() { 709 return html == null ? Utilities.escapeXml(message) : html; 710 } 711 712 public String getDisplay() { 713 return level + ": " + (location==null || location.isEmpty() ? "" : (location + ": ")) + message; 714 } 715 716 /** 717 * Returns a representation of this ValidationMessage suitable for logging. The values of 718 * most of the internal fields are included, so this may not be suitable for display to 719 * an end user. 720 */ 721 @Override 722 public String toString() { 723 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 724 b.append("level", level); 725 b.append("type", type); 726 b.append("location", location); 727 b.append("message", message); 728 return b.build(); 729 } 730 731 @Override 732 public boolean equals(Object o) { 733 return (this.getMessage() != null && this.getMessage().equals(((ValidationMessage)o).getMessage())) && (this.getLocation() != null && this.getLocation().equals(((ValidationMessage)o).getLocation())); 734 } 735 736 @Override 737 public int compare(ValidationMessage x, ValidationMessage y) { 738 String sx = x.getLevel().getDisplay() + x.getType().getDisplay() + String.format("%06d", x.getLine()) + x.getMessage(); 739 String sy = y.getLevel().getDisplay() + y.getType().getDisplay() + String.format("%06d", y.getLine()) + y.getMessage(); 740 return sx.compareTo(sy); 741 } 742 743 @Override 744 public int compareTo(ValidationMessage y) { 745 return compare(this, y); 746 } 747 748 public String getLocationLink() { 749 return locationLink; 750 } 751 752 public ValidationMessage setLocationLink(String locationLink) { 753 this.locationLink = locationLink; 754 return this; 755 } 756 757 public String getTxLink() { 758 return txLink; 759 } 760 761 public ValidationMessage setTxLink(String txLink) { 762 this.txLink = txLink; 763 return this; 764 } 765 766 public void setHtml(String html) { 767 this.html = html; 768 } 769 770 public boolean isSlicingHint() { 771 return slicingHint; 772 } 773 774 public ValidationMessage setSlicingHint(boolean slicingHint) { 775 this.slicingHint = slicingHint; 776 return this; 777 } 778 779 public String getSliceHtml() { 780 return sliceHtml; 781 } 782 783 public ValidationMessage setSliceHtml(String sliceHtml, String[] text) { 784 this.sliceHtml = sliceHtml; 785 this.sliceText = text; 786 return this; 787 } 788 789 public String getMessageId() { 790 return messageId; 791 } 792 793 public ValidationMessage setMessageId(String messageId) { 794 this.messageId = messageId; 795 return this; 796 } 797 798 public boolean isSignpost() { 799 return signpost; 800 } 801 802 public ValidationMessage setSignpost(boolean signpost) { 803 this.signpost = signpost; 804 return this; 805 } 806 807 public boolean isCriticalSignpost() { 808 return criticalSignpost; 809 } 810 811 public ValidationMessage setCriticalSignpost(boolean criticalSignpost) { 812 this.criticalSignpost = criticalSignpost; 813 return this; 814 } 815 816 public Date getRuleDate() { 817 return ruleDate; 818 } 819 820 public ValidationMessage setRuleDate(Date ruleDate) { 821 this.ruleDate = ruleDate; 822 return this; 823 } 824 825 826 public ValidationMessage setRuleDate(String value) { 827 if (value == null) { 828 ruleDate = null; 829 } else { 830 Date d = null; 831 try { 832 d = new SimpleDateFormat("yyyy-MM-dd").parse(value); 833 } catch (ParseException e) { 834 e.printStackTrace(); 835 } 836 ruleDate = d; 837 } 838 return this; 839 } 840}