class ConvertToSentenceCaseFilter extends RuleFilter {

  @Override
  public RuleMatch acceptRuleMatch(RuleMatch match, Map<String, String> arguments, int patternTokenPos,
                                   AnalyzedTokenReadings[] patternTokens, List<Integer> tokenPositions) throws IOException {
    RuleMatch ruleMatch = match;
    boolean firstDone = false;
    StringBuilder replacement = new StringBuilder();
    StringBuilder originalStr = new StringBuilder();
    for (int i = patternTokenPos; i < patternTokens.length; i++) {
      if (patternTokens[i].getStartPos() < match.getFromPos() || patternTokens[i].getEndPos() > match.getToPos()) {
        continue;
      }
      String normalizedCase = normalizedCase(patternTokens[i]);
      if (i + 1 < patternTokens.length && patternTokens[i + 1].getToken().equals(".")) {
        if (normalizedCase.length() == 1) {
          normalizedCase = normalizedCase.toUpperCase();
        } else if (normalizedCase.equals("corp")) {
          normalizedCase = "Corp";
        }
      }
      String tokenString = patternTokens[i].getToken();
      String tokenCapitalized = StringTools.uppercaseFirstChar(normalizedCase);
      if (!firstDone & !isPunctuation(tokenString) && !tokenString.isEmpty()) {
        firstDone = true;
        replacement.append(tokenCapitalized);
        originalStr.append(tokenString);
      } else {
        if (patternTokens[i].isWhitespaceBefore()) {
          replacement.append(" ");
          originalStr.append(" ");
        }
        replacement.append(normalizedCase);
        originalStr.append(tokenString);
      }
    }
    if (replacement.toString().equals(originalStr.toString())) {
      return null;
    }
    ruleMatch.setSuggestedReplacement(replacement.toString());
    return ruleMatch;
  }

  private boolean isPunctuation(String s) {
    return Pattern.matches("\\p{IsPunctuation}", s);
  }

  private String normalizedCase(AnalyzedTokenReadings atr) {<FILL_FUNCTION_BODY>}

  protected boolean tokenIsException(String s) {
    return false;
  }
}

    String tokenLowercase = atr.getToken().toLowerCase();
    if (atr.hasTypographicApostrophe()) {
      tokenLowercase = tokenLowercase.replaceAll("'", "’");
    }
    if (tokenIsException(tokenLowercase)) {
      // exception: the lemma is "I"
      return tokenLowercase;
    }
    String tokenCapitalized = StringTools.uppercaseFirstChar(tokenLowercase);
    //boolean lemmaIsAllUppercase = false;
    boolean lemmaIsCapitalized = false;
    boolean lemmaIsLowercase = false;
    for (AnalyzedToken at : atr) {
      if (at.hasNoTag() || at.getLemma() == null) {
        return tokenCapitalized;
      }
      // for multi-words lemmas, take the first word
      String lemma = at.getLemma().split(" ")[0];
      //lemmaIsAllUppercase = lemmaIsAllUppercase || StringTools.isAllUppercase(lemma);
      lemmaIsCapitalized = lemmaIsCapitalized || StringTools.isCapitalizedWord(lemma);
      lemmaIsLowercase = lemmaIsLowercase || !StringTools.isNotAllLowercase(lemma);
    }
    if (lemmaIsLowercase) {
      return tokenLowercase;
    }
    if (lemmaIsCapitalized) {
      return tokenCapitalized;
    }
    return atr.getToken();
  
<methods>public non-sealed void <init>() ,public abstract org.languagetool.rules.RuleMatch acceptRuleMatch(org.languagetool.rules.RuleMatch, Map<java.lang.String,java.lang.String>, int, org.languagetool.AnalyzedTokenReadings[], List<java.lang.Integer>) throws java.io.IOException,public boolean matches(Map<java.lang.String,java.lang.String>, org.languagetool.AnalyzedTokenReadings[], int, List<java.lang.Integer>) throws java.io.IOException<variables>