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