@NotNull
  @Override
  public Result beforeCharTyped(final char c,
                                @NotNull final Project project,
                                @NotNull final Editor editor,
                                @NotNull final PsiFile file,
                                @NotNull final FileType fileType) {
    if (!isJavaFile(file)) return Result.CONTINUE;

    if (c == '@') {
      autoPopupJavadocLookup(project, editor);
    }
    else if (c == '#' || c == '.') {
      autoPopupMemberLookup(project, editor);
    }

    int offsetBefore = editor.getCaretModel().getOffset();

    //important to calculate before inserting charTyped
    myJavaLTTyped = '<' == c &&
                    !isJspFile(file) &&
                    CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET &&
                    isLanguageLevel5OrHigher(file) &&
                    TypedHandlerUtil.isAfterClassLikeIdentifierOrDot(offsetBefore, editor, JavaTokenType.DOT, JavaTokenType.IDENTIFIER,
                                                                     true);

    if ('>' == c) {
      if (!isJspFile(file) && CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET && isLanguageLevel5OrHigher(file)) {
        if (TypedHandlerUtil.handleGenericGT(editor, JavaTokenType.LT, JavaTokenType.GT, JavaTypingTokenSets.INVALID_INSIDE_REFERENCE)) {
          return Result.STOP;
        }
      }
    }

    if (c == '?') {
      if (handleQuestionMark(project, editor, file, offsetBefore)) {
        return Result.STOP;
      }
    }

    if (c == '=') {
      if (handleEquality(project, editor, file, offsetBefore)) {
        return Result.STOP;
      }
    }

    if (c == ';') {
      if (handleSemicolon(project, editor, file, fileType)) return Result.STOP;
    }
    if (fileType instanceof JavaFileType && c == '}') {
      // Normal RBrace handler doesn't work with \{}, because braces in string template are not separate tokens
      int offset = editor.getCaretModel().getOffset();

      HighlighterIterator iterator = editor.getHighlighter().createIterator(offset);
      CharSequence sequence = editor.getDocument().getCharsSequence();
      if (!iterator.atEnd() && iterator.getStart() == offset &&
          (iterator.getTokenType() == JavaTokenType.STRING_TEMPLATE_END ||
           iterator.getTokenType() == JavaTokenType.TEXT_BLOCK_TEMPLATE_END ||
           iterator.getTokenType() == JavaTokenType.STRING_TEMPLATE_MID ||
           iterator.getTokenType() == JavaTokenType.TEXT_BLOCK_TEMPLATE_MID)) {
        if (sequence.length() > offset && sequence.charAt(offset) == '}') {
          editor.getCaretModel().moveToOffset(offset + 1);
          return Result.STOP;
        }
      }
    }
    if (fileType instanceof JavaFileType && c == '{') {
      int offset = editor.getCaretModel().getOffset();
      if (offset == 0) {
        return Result.CONTINUE;
      }

      HighlighterIterator iterator = editor.getHighlighter().createIterator(offset - 1);
      while (!iterator.atEnd() && iterator.getTokenType() == TokenType.WHITE_SPACE) {
        iterator.retreat();
      }
      if (iterator.atEnd() || iterator.getTokenType() == JavaTokenType.RBRACKET || iterator.getTokenType() == JavaTokenType.EQ) {
        return Result.CONTINUE;
      }
      Document doc = editor.getDocument();
      if (!iterator.atEnd() &&
          (iterator.getTokenType() == StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN ||
           iterator.getTokenType() == StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN) &&
          CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) { // "\{}" in strings
        CharSequence sequence = doc.getCharsSequence();
        if (sequence.charAt(offset - 1) == '\\' && (sequence.length() == offset || sequence.charAt(offset) != '}')) {
          doc.insertString(offset, "{}");
          editor.getCaretModel().moveToOffset(offset + 1);
          return Result.STOP;
        }
      }
      PsiDocumentManager.getInstance(project).commitDocument(doc);
      final PsiElement leaf = file.findElementAt(offset);
      if (BasicJavaAstTreeUtil.getParentOfType(leaf, BASIC_ARRAY_INITIALIZER_EXPRESSION, false,
                                               ParentAwareTokenSet.orSet(ParentAwareTokenSet.create(BASIC_CODE_BLOCK), MEMBER_SET)) !=
          null) {
        return Result.CONTINUE;
      }
      PsiElement st = leaf != null ? leaf.getParent() : null;
      PsiElement prev = offset > 1 ? file.findElementAt(offset - 1) : null;
      if (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET && isRparenth(leaf) &&
          st != null &&
          (BasicJavaAstTreeUtil.is(st.getNode(), BASIC_WHILE_STATEMENT) ||
           BasicJavaAstTreeUtil.is(st.getNode(), BASIC_IF_STATEMENT)) &&
          shouldInsertStatementBody(st, doc, prev)) {
        return processWhileAndIfStatementBody(project, editor, file);
      }

      if (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET && afterArrowInCase(leaf) &&
          iterator.getTokenType() != JavaTokenType.LBRACKET) {
        Result stop = processOpenBraceInOneLineCaseRule(project, editor, file, leaf);
        if (stop != null) return stop;
      }

      if (BasicJavaAstTreeUtil.getParentOfType(leaf, BASIC_CODE_BLOCK, false, MEMBER_SET) != null &&
          !shouldInsertPairedBrace(leaf)) {
        EditorModificationUtilEx.insertStringAtCaret(editor, "{");
        TypedHandler.indentOpenedBrace(project, editor);
        return Result.STOP; // use case: manually wrapping part of method's code in 'if', 'while', etc
      }
    }

    return Result.CONTINUE;
  }
@NotNull
  @Override
  public Result beforeCharTyped(final char c,
                                @NotNull final Project project,
                                @NotNull final Editor editor,
                                @NotNull final PsiFile file,
                                @NotNull final FileType fileType)