class StringValidator extends FXAbstractValidator<TextInputControl, FXString> {

  @Override
  public void validate(TextInputControl control, FXString annotation) throws ValidationException {
    // shortcut: do not check if disabled.
    if (control.isDisabled()) {
      this.isValid.set(true);
      return;
    }
    if (!control.isVisible()) {
      this.isValid.set(true);
      return;
    }

    boolean valid = true;

    // 1. minLength?
    if (annotation.minLength() > 0) {
      valid = valid && control.getText().length() >= annotation.minLength();
      
      this.isValid.set(valid);
      if (!valid) {
        String msg = annotation.messageMinLength();
        if (annotation.messageMinLength().contains("%d")) {
          msg = String.format(annotation.messageMinLength(), annotation.minLength());
        }
        throw new ValidationException(msg);
      }
    }

    // 2. maxLength?
    if (annotation.maxLength() > 0) {
      valid = valid && control.getText().length() <= annotation.maxLength();
      this.isValid.set(valid);
      if (!valid) {
        String msg = annotation.messageMaxLength();
        if (annotation.messageMaxLength().contains("%d")) {
          msg = String.format(annotation.messageMaxLength(), annotation.maxLength());
        }
        throw new ValidationException(msg);
      }
    }

    // 3. pattern?
    if (annotation.pattern().length() > 0) {
      // check for necessary lazy initialization: 
      if (this.pattern == null) {
        this.createRegex(annotation);
      }
      
      valid = valid && this.pattern.matcher(control.getText()).matches();
      this.isValid.set(valid);
      if (!valid) {
        String msg = annotation.messagePattern();
        throw new ValidationException(msg);
      }
    }
  }

  public  StringValidator();
  public  StringValidator(TextInputControl control, FXString annotation);

}