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