001package org.hl7.fhir.dstu3.validation; 002 003/* 004Copyright (c) 2011+, HL7, Inc 005All rights reserved. 006 007Redistribution and use in source and binary forms, with or without modification, 008are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028POSSIBILITY OF SUCH DAMAGE. 029 030*/ 031 032import java.text.MessageFormat; 033import java.util.List; 034 035import org.apache.commons.lang3.StringUtils; 036import org.hl7.fhir.utilities.validation.ValidationMessage; 037import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 038import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 039import org.hl7.fhir.utilities.validation.ValidationMessage.Source; 040 041public class BaseValidator { 042 043 protected Source source; 044 045 /** 046 * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails 047 * 048 * @param thePass 049 * Set this parameter to <code>false</code> if the validation does not pass 050 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 051 */ 052 protected boolean fail(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { 053 if (!thePass) { 054 errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.FATAL)); 055 } 056 return thePass; 057 } 058 059 /** 060 * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails 061 * 062 * @param thePass 063 * Set this parameter to <code>false</code> if the validation does not pass 064 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 065 */ 066 protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) { 067 if (!thePass) { 068 String path = toPath(pathParts); 069 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL)); 070 } 071 return thePass; 072 } 073 074 /** 075 * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails 076 * 077 * @param thePass 078 * Set this parameter to <code>false</code> if the validation does not pass 079 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 080 */ 081 protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) { 082 if (!thePass) { 083 String path = toPath(pathParts); 084 errors.add(new ValidationMessage(source, type, -1, -1, path, formatMessage(theMessage, theMessageArguments), IssueSeverity.FATAL)); 085 } 086 return thePass; 087 } 088 089 /** 090 * Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails 091 * 092 * @param thePass 093 * Set this parameter to <code>false</code> if the validation does not pass 094 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 095 */ 096 protected boolean fail(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { 097 if (!thePass) { 098 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL)); 099 } 100 return thePass; 101 } 102 103 104 private String formatMessage(String theMessage, Object... theMessageArguments) { 105 String message; 106 if (theMessageArguments != null && theMessageArguments.length > 0) { 107 message = MessageFormat.format(theMessage, theMessageArguments); 108 } else { 109 message = theMessage; 110 } 111 return message; 112 } 113 114 protected boolean grammarWord(String w) { 115 return w.equals("and") || w.equals("or") || w.equals("a") || w.equals("the") || w.equals("for") || w.equals("this") || w.equals("that") || w.equals("of"); 116 } 117 118 /** 119 * Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails 120 * 121 * @param thePass 122 * Set this parameter to <code>false</code> if the validation does not pass 123 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 124 */ 125 protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) { 126 if (!thePass) { 127 errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION)); 128 } 129 return thePass; 130 } 131 132 /** 133 * Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails 134 * 135 * @param thePass 136 * Set this parameter to <code>false</code> if the validation does not pass 137 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 138 */ 139 protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { 140 if (!thePass) { 141 String message = formatMessage(theMessage, theMessageArguments); 142 errors.add(new ValidationMessage(source, type, line, col, path, message, IssueSeverity.INFORMATION)); 143 } 144 return thePass; 145 } 146 147 /** 148 * Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails 149 * 150 * @param thePass 151 * Set this parameter to <code>false</code> if the validation does not pass 152 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 153 */ 154 protected boolean hint(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) { 155 if (!thePass) { 156 String path = toPath(pathParts); 157 String message = formatMessage(theMessage, theMessageArguments); 158 errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.INFORMATION)); 159 } 160 return thePass; 161 } 162 163 /** 164 * Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails 165 * 166 * @param thePass 167 * Set this parameter to <code>false</code> if the validation does not pass 168 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 169 */ 170 protected boolean hint(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { 171 if (!thePass) { 172 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION)); 173 } 174 return thePass; 175 } 176 177 /** 178 * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails 179 * 180 * @param thePass 181 * Set this parameter to <code>false</code> if the validation does not pass 182 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 183 */ 184 protected boolean rule(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) { 185 if (!thePass) { 186 String message = formatMessage(theMessage, theMessageArguments); 187 errors.add(new ValidationMessage(source, type, line, col, path, message, IssueSeverity.ERROR)); 188 } 189 return thePass; 190 } 191 192 /** 193 * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails 194 * 195 * @param thePass 196 * Set this parameter to <code>false</code> if the validation does not pass 197 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 198 */ 199 protected boolean rule(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) { 200 if (!thePass) { 201 String path = toPath(pathParts); 202 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR)); 203 } 204 return thePass; 205 } 206 207 /** 208 * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails 209 * 210 * @param thePass 211 * Set this parameter to <code>false</code> if the validation does not pass 212 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 213 */ 214 protected boolean rule(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) { 215 if (!thePass) { 216 String path = toPath(pathParts); 217 String message = formatMessage(theMessage, theMessageArguments); 218 errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.ERROR)); 219 } 220 return thePass; 221 } 222 223 /** 224 * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails 225 * 226 * @param thePass 227 * Set this parameter to <code>false</code> if the validation does not pass 228 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 229 */ 230 protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { 231 if (!thePass) { 232 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR)); 233 } 234 return thePass; 235 } 236 237 static public boolean rule(List<ValidationMessage> errors, Source source, IssueType type, String path, boolean thePass, String msg) { 238 if (!thePass) { 239 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR)); 240 } 241 return thePass; 242 } 243 244 /** 245 * Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails 246 * 247 * @param thePass 248 * Set this parameter to <code>false</code> if the validation does not pass 249 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 250 */ 251 protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) { 252 if (!thePass) { 253 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.ERROR)); 254 } 255 return thePass; 256 } 257 258 protected String splitByCamelCase(String s) { 259 StringBuilder b = new StringBuilder(); 260 for (int i = 0; i < s.length(); i++) { 261 char c = s.charAt(i); 262 if (Character.isUpperCase(c) && !(i == 0 || Character.isUpperCase(s.charAt(i-1)))) 263 b.append(' '); 264 b.append(c); 265 } 266 return b.toString(); 267 } 268 269 protected String stripPunctuation(String s, boolean numbers) { 270 StringBuilder b = new StringBuilder(); 271 for (char c : s.toCharArray()) { 272 int t = Character.getType(c); 273 if (t == Character.UPPERCASE_LETTER || t == Character.LOWERCASE_LETTER || t == Character.TITLECASE_LETTER || t == Character.MODIFIER_LETTER || t == Character.OTHER_LETTER || (t == Character.DECIMAL_DIGIT_NUMBER && numbers) || (t == Character.LETTER_NUMBER && numbers) || c == ' ') 274 b.append(c); 275 } 276 return b.toString(); 277 } 278 279 private String toPath(List<String> pathParts) { 280 if (pathParts == null || pathParts.isEmpty()) { 281 return ""; 282 } 283 return "//" + StringUtils.join(pathParts, '/'); 284 } 285 286 /** 287 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 288 * 289 * @param thePass 290 * Set this parameter to <code>false</code> if the validation does not pass 291 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 292 */ 293 protected boolean warning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) { 294 if (!thePass) { 295 msg = formatMessage(msg, theMessageArguments); 296 errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.WARNING)); 297 } 298 return thePass; 299 300 } 301 302 protected boolean warningOrError(boolean isError, List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) { 303 if (!thePass) { 304 msg = formatMessage(msg, theMessageArguments); 305 errors.add(new ValidationMessage(source, type, line, col, path, msg, isError ? IssueSeverity.ERROR : IssueSeverity.WARNING)); 306 } 307 return thePass; 308 309 } 310 311 /** 312 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 313 * 314 * @param thePass 315 * Set this parameter to <code>false</code> if the validation does not pass 316 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 317 */ 318 protected boolean warning(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) { 319 if (!thePass) { 320 String path = toPath(pathParts); 321 String message = formatMessage(theMessage, theMessageArguments); 322 errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.WARNING)); 323 } 324 return thePass; 325 } 326 327 /** 328 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 329 * 330 * @param thePass 331 * Set this parameter to <code>false</code> if the validation does not pass 332 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 333 */ 334 protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { 335 if (!thePass) { 336 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.WARNING)); 337 } 338 return thePass; 339 } 340 341 /** 342 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 343 * 344 * @param thePass 345 * Set this parameter to <code>false</code> if the validation does not pass 346 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 347 */ 348 protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) { 349 if (!thePass) { 350 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING)); 351 } 352 return thePass; 353 } 354 355 /** 356 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 357 * 358 * @param thePass 359 * Set this parameter to <code>false</code> if the validation does not pass 360 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 361 */ 362 protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) { 363 if (!thePass) { 364 msg = formatMessage(msg, theMessageArguments); 365 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING)); 366 } 367 return thePass; 368 } 369 370 //--------- 371 /** 372 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 373 * 374 * @param thePass 375 * Set this parameter to <code>false</code> if the validation does not pass 376 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 377 */ 378 protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) { 379 if (!thePass) { 380 msg = formatMessage(msg, theMessageArguments); 381 errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION)); 382 } 383 return thePass; 384 385 } 386 387 /** 388 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 389 * 390 * @param thePass 391 * Set this parameter to <code>false</code> if the validation does not pass 392 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 393 */ 394 protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) { 395 if (!thePass) { 396 String path = toPath(pathParts); 397 String message = formatMessage(theMessage, theMessageArguments); 398 errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.INFORMATION)); 399 } 400 return thePass; 401 } 402 403 /** 404 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 405 * 406 * @param thePass 407 * Set this parameter to <code>false</code> if the validation does not pass 408 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 409 */ 410 protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) { 411 if (!thePass) { 412 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION)); 413 } 414 return thePass; 415 } 416 417 /** 418 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 419 * 420 * @param thePass 421 * Set this parameter to <code>false</code> if the validation does not pass 422 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 423 */ 424 protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) { 425 if (!thePass) { 426 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION)); 427 } 428 return thePass; 429 } 430 431 /** 432 * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails 433 * 434 * @param thePass 435 * Set this parameter to <code>false</code> if the validation does not pass 436 * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation) 437 */ 438 protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) { 439 if (!thePass) { 440 msg = formatMessage(msg, theMessageArguments); 441 errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION)); 442 } 443 return thePass; 444 } 445 446}