001package org.hl7.fhir.dstu2016may.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.IssueType;
038import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
039import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
040
041public class BaseValidator {
042
043  protected ValidationMessage.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, ValidationMessage.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, ValidationMessage.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, ValidationMessage.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, ValidationMessage.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  /**
303   * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
304   * 
305   * @param thePass
306   *          Set this parameter to <code>false</code> if the validation does not pass
307   * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
308   */
309  protected boolean warning(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
310    if (!thePass) {
311      String path = toPath(pathParts);
312      String message = formatMessage(theMessage, theMessageArguments);
313      errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.WARNING));
314    }
315    return thePass;
316  }
317
318  /**
319   * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
320   * 
321   * @param thePass
322   *          Set this parameter to <code>false</code> if the validation does not pass
323   * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
324   */
325  protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
326    if (!thePass) {
327      errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.WARNING));
328    }
329    return thePass;
330  }
331
332  /**
333   * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
334   * 
335   * @param thePass
336   *          Set this parameter to <code>false</code> if the validation does not pass
337   * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
338   */
339  protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
340    if (!thePass) {
341      errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING));
342    }
343    return thePass;
344  }
345
346  /**
347   * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
348   * 
349   * @param thePass
350   *          Set this parameter to <code>false</code> if the validation does not pass
351   * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
352   */
353  protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
354    if (!thePass) {
355      msg = formatMessage(msg, theMessageArguments);
356      errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING));
357    }
358    return thePass;
359  }
360
361  //---------
362  /**
363   * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
364   * 
365   * @param thePass
366   *          Set this parameter to <code>false</code> if the validation does not pass
367   * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
368   */
369  protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
370    if (!thePass) { 
371      msg = formatMessage(msg, theMessageArguments);
372      errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION));
373    }
374    return thePass;
375
376  }
377
378  /**
379   * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
380   * 
381   * @param thePass
382   *          Set this parameter to <code>false</code> if the validation does not pass
383   * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
384   */
385  protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
386    if (!thePass) {
387      String path = toPath(pathParts);
388      String message = formatMessage(theMessage, theMessageArguments);
389      errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.INFORMATION));
390    }
391    return thePass;
392  }
393
394  /**
395   * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
396   * 
397   * @param thePass
398   *          Set this parameter to <code>false</code> if the validation does not pass
399   * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
400   */
401  protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
402    if (!thePass) {
403      errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION));
404    }
405    return thePass;
406  }
407
408  /**
409   * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
410   * 
411   * @param thePass
412   *          Set this parameter to <code>false</code> if the validation does not pass
413   * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
414   */
415  protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
416    if (!thePass) {
417      errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION));
418    }
419    return thePass;
420  }
421
422  /**
423   * Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
424   * 
425   * @param thePass
426   *          Set this parameter to <code>false</code> if the validation does not pass
427   * @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
428   */
429  protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
430    if (!thePass) {
431      msg = formatMessage(msg, theMessageArguments);
432      errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION));
433    }
434    return thePass;
435  }
436
437}