class SimpleReplaceRule extends AbstractSimpleReplaceRule {

  private static final Map<String, List<String>> wrongWords = loadFromPath("/uk/replace.txt");
//  private static final Set<String> FORCE_REPLACE_LIST = new HashSet<>(Arrays.asList("главком"));
  private final MorfologikUkrainianSpellerRule morfologikSpellerRule;

  @Override
  public Map<String, List<String>> getWrongWords() {
    return wrongWords;
  }

  public SimpleReplaceRule(ResourceBundle messages, MorfologikUkrainianSpellerRule morfologikSpellerRule,
                           final Language language) throws IOException {
    super(messages, language);
    setIgnoreTaggedWords();
    this.morfologikSpellerRule = morfologikSpellerRule;
  }

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

  @Override
  public String getDescription() {
    return "Пошук помилкових слів";
  }

  @Override
  public String getShort() {
    return "Помилка?";
  }

  @Override
  public String getMessage(String tokenStr, List<String> replacements) {
    return "«" + tokenStr + "» - помилкове слово, виправлення: "
        + String.join(", ", replacements) + ".";
  }

  @Override
  protected boolean isTagged(AnalyzedTokenReadings tokenReadings) {
//    if( FORCE_REPLACE_LIST.contains(tokenReadings.getToken()) )
//      return false;
    
    for (AnalyzedToken token: tokenReadings.getReadings()) {
      // optimize
      if ( token.hasNoTag() )
        return false;

      String posTag = token.getPOSTag();
      if (isGoodPosTag(posTag)) {
        return true;
      }
    }

    return false;
  }

  @Override
  protected List<RuleMatch> findMatches(AnalyzedTokenReadings tokenReadings, AnalyzedSentence sentence) throws IOException {<FILL_FUNCTION_BODY>}

  private boolean isGoodPosTag(String posTag) {
    return posTag != null
        && !JLanguageTool.PARAGRAPH_END_TAGNAME.equals(posTag)
        && !JLanguageTool.SENTENCE_END_TAGNAME.equals(posTag)
        && !posTag.contains(IPOSTag.bad.getText())
        && !posTag.contains("subst")
        && !posTag.startsWith("<");
  }

  @Override
  public boolean isCaseSensitive() {
    return false;
  }

}

    List<RuleMatch> matches = super.findMatches(tokenReadings, sentence);
    if( matches.isEmpty() ) {
      if( PosTagHelper.hasPosTag(tokenReadings, Pattern.compile(".*?adjp:actv.*?:bad.*")) ) {
        String msg = "Активні дієприкметники не властиві українській мові.";
        String url;

        if( tokenReadings.getAnalyzedToken(0).getLemma().endsWith("ший") ) {
          msg += " Їх можна замінити на що + дієслово (випавший сніг - сніг, що випав), або на форму з суфіксом -л- (промокший - промоклий)";
          url = "http://padaread.com/?book=53784&pg=94";
        }
        else { // -ючий, -ющий тощо
          msg += " Їх можна замінити питомими словами в різний спосіб: що + дієслово (роблячий  - що робить), дієслівний корінь+ суфікси -льн-, -лив- тощо (збираючий - збиральний, обтяжуючий - обтяжливий),"
          + " заміна іменником (завідуючий - завідувач), заміна прикметником із відповідним значенням (діюча модель - робоча модель), зміна конструкції (з наступаючим Новим роком - з настанням Нового року) тощо.";
          url = "http://nbuv.gov.ua/j-pdf/Nchnpu_8_2013_5_2.pdf";
        }

        RuleMatch match = new RuleMatch(this, sentence, tokenReadings.getStartPos(), tokenReadings.getStartPos()
            + tokenReadings.getToken().length(), msg, getShort());

        match.setUrl(Tools.getUrl(url));

        matches.add(match);
      }
      else {
        if( PosTagHelper.hasPosTagPart(tokenReadings, ":bad") 
            && ! PosTagHelper.hasPosTagStart(tokenReadings, "number") ) {
            String msg = "Неправильно написане слово.";

            RuleMatch match = new RuleMatch(this, sentence, tokenReadings.getStartPos(), tokenReadings.getStartPos()
                + tokenReadings.getToken().length(), msg, getShort());
            
            List<String> suggestions = morfologikSpellerRule.getSpeller1().getSuggestionsFromDefaultDicts(tokenReadings.getToken());
            suggestions.removeIf(s -> s.contains(" "));
            match.setSuggestedReplacements(suggestions);
//            RuleMatch[] spellerMatches = morfologikSpellerRule.match(new AnalyzedSentence(new AnalyzedTokenReadings[] {tokenReadings}));
//            if( spellerMatches.length > 0 ) {
//              match.setSuggestedReplacements(spellerMatches[0].getSuggestedReplacements());
//            }
            matches.add(match);
        }
      }
    }
//    else {
//      if( PosTagHelper.hasPosTag(tokenReadings, Pattern.compile("(?!verb).*:subst")) ) {
//        for(int i=0; i<matches.size(); i++) {
//          RuleMatch match = matches.get(i);
//          RuleMatch newMatch = new RuleMatch(match.getRule(), match.getSentence(), match.getFromPos(), match.getToPos(), "Ця форма не відповідає мовній нормі");
//          newMatch.setSuggestedReplacements(match.getSuggestedReplacements());
//          matches.set(i, newMatch);
//        }
//      }
//    }
    return matches;
  
<methods>public void <init>(java.util.ResourceBundle, org.languagetool.Language) ,public java.lang.String getDescription() ,public java.lang.String getId() ,public java.util.Locale getLocale() ,public java.lang.String getMessage(java.lang.String, List<java.lang.String>) ,public java.lang.String getShort() ,public org.languagetool.synthesis.Synthesizer getSynthesizer() ,public abstract Map<java.lang.String,List<java.lang.String>> getWrongWords() ,public boolean isCaseSensitive() ,public boolean isCheckLemmas() ,public org.languagetool.rules.RuleMatch[] match(org.languagetool.AnalyzedSentence) throws java.io.IOException,public void setCheckLemmas(boolean) ,public void setIgnoreTaggedWords() ,public void useSubRuleSpecificIds() <variables>private boolean checkLemmas,protected boolean ignoreTaggedWords,private final non-sealed org.languagetool.Language language,private static final Logger logger,protected boolean subRuleSpecificIds