class CompoundCoherencyRule extends TextLevelRule {

  private static final Pattern HYPHEN = Pattern.compile("-");

  public CompoundCoherencyRule(ResourceBundle messages) {
    super.setCategory(Categories.STYLE.getCategory(messages));
    addExamplePair(Example.wrong("Ein Helpdesk gliedert sich in verschiedene Level. Die Qualität des <marker>Help-Desks</marker> ist wichtig."),
                   Example.fixed("Ein Helpdesk gliedert sich in verschiedene Level. Die Qualität des <marker>Helpdesks</marker> ist wichtig."));
  }

  @Override
  public String getId() {
    return "DE_COMPOUND_COHERENCY";
  }

  @Override
  public String getDescription() {
    return "Einheitliche Schreibweise bei Komposita (mit oder ohne Bindestrich)";
  }

  @Override
  public RuleMatch[] match(List<AnalyzedSentence> sentences) throws IOException {
    List<RuleMatch> ruleMatches = new ArrayList<>();
    Map<String,List<String>> normToTextOccurrences = new HashMap<>();
    int pos = 0;
    for (AnalyzedSentence sentence : sentences) {
      AnalyzedTokenReadings[] tokens = sentence.getTokensWithoutWhitespace();
      for (AnalyzedTokenReadings atr : tokens) {
        String lemmaOrNull = getLemma(atr);
        String token = atr.getToken();
        if (token.isEmpty()) {
          continue;
        }
        // The whole implementation could be simpler, but this way we also catch cases where
        // the word (and thus its lemma) isn't known.
        String lemma = lemmaOrNull != null ? lemmaOrNull : token;
        String normToken = HYPHEN.matcher(lemma).replaceAll("").toLowerCase();
        if (StringUtils.isNumeric(normToken)) {
          // avoid messages about "2-3" and "23" both being used
          break;
        }
        List<String> textOcc = normToTextOccurrences.get(normToken);
        if (textOcc != null) {
          if (textOcc.stream().noneMatch(f -> f.equalsIgnoreCase(lemma))) {
            String other = textOcc.get(0);
            if (containsHyphenInside(other) || containsHyphenInside(token)) {
              String msg = "Uneinheitliche Verwendung von Bindestrichen. Der Text enthält sowohl '" + token + "' als auch '" + other + "'.";
              RuleMatch ruleMatch = new RuleMatch(this, sentence, pos + atr.getStartPos(), pos + atr.getEndPos(), msg);
              if (HYPHEN.matcher(token).replaceAll("").equalsIgnoreCase(HYPHEN.matcher(other).replaceAll(""))) {
                // might be different inflected forms, so only suggest if really just the hyphen is different:
                ruleMatch.setSuggestedReplacement(other);
              }
              ruleMatches.add(ruleMatch);
            }
          }
        } else {
          List<String> l = new ArrayList<>();
          l.add(lemma);
          normToTextOccurrences.putIfAbsent(normToken, l);
        }
      }
      pos += sentence.getCorrectedTextLength();
    }
    return toRuleMatchArray(ruleMatches);
  }

  private boolean containsHyphenInside(String token) {
    return token.contains("-") && !token.startsWith("-") && !token.endsWith("-");
  }

  @Nullable
  private String getLemma(AnalyzedTokenReadings atr) {<FILL_FUNCTION_BODY>}

  @Override
  public int minToCheckParagraph() {
    return -1;
  }

}

    String lemmaOrNull = atr.hasSameLemmas() && atr.getReadingsLength() > 0 ? atr.getReadings().get(0).getLemma() : null;
    if (lemmaOrNull != null) {
      // our analysis gets lemma "Jugendfoto" for "Jugend-Fotos", we 'fix' that here
      String token = atr.getToken();
      if (!lemmaOrNull.contains("-") && token.contains("-")) {
        StringBuilder lemmaBuilder = new StringBuilder();
        for (int lemmaPos = 0, tokenPos = 0; lemmaPos < lemmaOrNull.length(); lemmaPos++, tokenPos++) {
          if (tokenPos >= token.length()) {
            break;
          }
          char lemmaChar = lemmaOrNull.charAt(lemmaPos);
          char tokenChar = token.charAt(tokenPos);
          if (lemmaChar == tokenChar) {
            lemmaBuilder.append(lemmaChar);
          } else if (token.charAt(tokenPos) == '-') {
            tokenPos++;  // skip hyphen
            lemmaBuilder.append('-');
            if (lemmaPos + 1 < token.length() && Character.isUpperCase(token.charAt(tokenPos))) {
              lemmaBuilder.append(Character.toUpperCase(lemmaChar));
            } else {
              lemmaBuilder.append(lemmaChar);
            }
          }
        }
        return lemmaBuilder.toString();
      }
      return lemmaOrNull;
    } else {
      return null;
    }
  
<methods>public void <init>() ,public void <init>(java.util.ResourceBundle) ,public int estimateContextForSureMatch() ,public org.languagetool.rules.RuleMatch[] match(List<org.languagetool.AnalyzedSentence>, org.languagetool.markup.AnnotatedText) throws java.io.IOException,public abstract org.languagetool.rules.RuleMatch[] match(List<org.languagetool.AnalyzedSentence>) throws java.io.IOException,public final org.languagetool.rules.RuleMatch[] match(org.languagetool.AnalyzedSentence) throws java.io.IOException,public abstract int minToCheckParagraph() <variables>