@NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
    return new JavaElementVisitor() {
      @Override
      public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) {
        if (PATTERN_METHODS.test(call)) {
          processExpression(call.getArgumentList().getExpressions()[0]);
        }
      }

      private void processExpression(@NotNull PsiExpression expression) {
        Object patternObject = ExpressionUtils.computeConstantExpression(expression);
        if (!(patternObject instanceof String pattern)) return;
        record Token(char character, int pos, int length) {}
        List<Token> tokens = new ArrayList<>();
        int optionalDepth = 0;
        char[] array = pattern.toCharArray();
        boolean inQuote = false;
        int lastQuoteIndex = 0;
        int length = 0;
        for (int pos = 0; pos < array.length; pos++) {
          char c = array[pos];

          //process tokens
          if (!inQuote && isLetter(c)) {
            length++;
            if (pos != array.length - 1 && c == array[pos + 1]) {
              continue;
            }
            else {
              tokens.add(new Token(c, pos - length + 1, length));
              checkPadding(expression, array, pos - length + 1, pos + 1);
            }
          }
          length = 0;


          //check if it starts quote region or one quote
          //case with one quote ('') is processed as quote region
          if (c == '\'') {
            inQuote = !inQuote;
            lastQuoteIndex = pos;
            continue;
          }
          //skip whole region in quote
          if (inQuote) {
            continue;
          }

          checkUnsupportedSymbolsProblem(expression, pos, c);

          if (c == '[') {
            optionalDepth++;
          }
          if (c == ']') {
            optionalDepth--;
            checkOptionalDepthProblem(expression, optionalDepth, pos);
            if (optionalDepth < 0) {
              //reset after problem
              optionalDepth = 0;
            }
          }
        }

        checkInQuoteProblem(expression, inQuote, lastQuoteIndex);

        for (Token token : tokens) {
          checkAvailableCount(expression, token.character, token.pos, token.length);
        }
      }

      private void checkOptionalDepthProblem(@NotNull PsiExpression expression, int optionalDepth, int pos) {
        if (optionalDepth < 0) {
          holder.registerProblem(expression, ExpressionUtils.findStringLiteralRange(expression, pos, pos + 1),
                                 InspectionGadgetsBundle.message("inspection.incorrect.date.format.message.unpaired", '['));
        }
      }

      private void checkInQuoteProblem(@NotNull PsiExpression expression, boolean inQuote, int pos) {
        if (inQuote) {
          holder.registerProblem(expression, ExpressionUtils.findStringLiteralRange(expression, pos, pos + 1),
                                 InspectionGadgetsBundle.message("inspection.incorrect.date.format.message.literal", '\''));
        }
      }

      private void checkUnsupportedSymbolsProblem(@NotNull PsiExpression expression, int pos, char c) {
        if (c == '{' || c == '}' || c == '#') {
          holder.registerProblem(expression, ExpressionUtils.findStringLiteralRange(expression, pos, pos + 1),
                                 InspectionGadgetsBundle.message("inspection.incorrect.date.format.message.reserved.character", c));
        }
      }

      private void checkPadding(@NotNull PsiExpression expression, char[] array, int from, int to) {
        if (array[from] == 'p' && (to >= array.length || !isLetter(array[to]))) {
          holder.registerProblem(expression, ExpressionUtils.findStringLiteralRange(expression, from, to),
                                 InspectionGadgetsBundle.message("inspection.incorrect.date.format.message.padding", 'p'));
        }
      }

      private static boolean isLetter(char c) {
        return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
      }

      private void checkAvailableCount(PsiExpression expression, char character, int pos, int length) {
        if (character == 'p') {
          //checked during tokenizing
          return;
        }
        BiFunction<Character, Integer, @InspectionMessage String> verifier = ALLOWED_DATE_TIME_FORMATTER.get(character);
        if (verifier == null) {
          holder.registerProblem(expression, ExpressionUtils.findStringLiteralRange(expression, pos, pos + length),
                                 InspectionGadgetsBundle.message("inspection.incorrect.date.format.message.unsupported", character));
        }
        else {
          String message = verifier.apply(character, length);
          if (message != null) {
            final TextRange range = ExpressionUtils.findStringLiteralRange(expression, pos, pos + length);
            holder.registerProblem(expression, range, message);
          }
        }
      }
    };
  }
@NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly)