code: /* LanguageTool, a natural language style checker 
   * Copyright (C) 2020 Daniel Naber (http://www.danielnaber.de)
   * 
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
   * USA
   */
  
  package org.languagetool.rules;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  import java.util.Map;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import org.languagetool.AnalyzedToken;
  import org.languagetool.AnalyzedTokenReadings;
  import org.languagetool.Language;
  import org.languagetool.rules.patterns.AbstractPatternRule;
  import org.languagetool.rules.patterns.RuleFilter;
  import org.languagetool.synthesis.Synthesizer;
  import org.languagetool.tools.StringTools;
  
  /*
   * Synthesize suggestions using the lemma from one token (lemma_from) 
   * and the POS tag from another one (postag_from).
   * 
   * The lemma_select and postag_select attributes are required 
   * to choose one among several possible readings.
   */
  public abstract class AbstractAdvancedSynthesizerFilter extends RuleFilter {
  
    abstract protected Synthesizer getSynthesizer();
  
    @Override
    public RuleMatch acceptRuleMatch(RuleMatch match, Map<String, String> arguments, int patternTokenPos,
        AnalyzedTokenReadings[] patternTokens) throws IOException {
      
  //    if (match.getSentence().getText().contains("Jo pensem")) {
  //      int ii=0;
  //      ii++;
  //    }
  
      String postagSelect = getRequired("postagSelect", arguments);
      String lemmaSelect = getRequired("lemmaSelect", arguments);
      String postagFromStr = getRequired("postagFrom", arguments);
      String lemmaFromStr = getRequired("lemmaFrom", arguments);
      
      int postagFrom = 0;
      if (postagFromStr.startsWith("marker")) {
        while (postagFrom < patternTokens.length && patternTokens[postagFrom].getStartPos() < match.getFromPos()) {
          postagFrom++;
        }
        postagFrom++;
        if (postagFromStr.length()>6) {
          postagFrom += Integer.parseInt(postagFromStr.replace("marker", ""));
        }
      } else {
        postagFrom = Integer.parseInt(postagFromStr);
      }
      if (postagFrom < 1 || postagFrom > patternTokens.length) {
        throw new IllegalArgumentException("AdvancedSynthesizerFilter: Index out of bounds in "
            + match.getRule().getFullId() + ", value: " + postagFromStr);
      }
      int lemmaFrom = 0;
      if (lemmaFromStr.startsWith("marker")) {
        while (lemmaFrom < patternTokens.length && patternTokens[lemmaFrom].getStartPos() < match.getFromPos()) {
          lemmaFrom++;
        }
        lemmaFrom++;
        if (lemmaFromStr.length()>6) {
          lemmaFrom += Integer.parseInt(lemmaFromStr.replace("marker", ""));
        }
      } else {
        lemmaFrom = Integer.parseInt(lemmaFromStr);
      }
      if (lemmaFrom < 1 || lemmaFrom > patternTokens.length) {
        throw new IllegalArgumentException("AdvancedSynthesizerFilter: Index out of bounds in "
            + match.getRule().getFullId() + ", value: " + lemmaFromStr);
      }
  
      String postagReplace = getOptional("postagReplace", arguments);
  
      String desiredLemma = getAnalyzedToken(patternTokens[lemmaFrom - 1], lemmaSelect).getLemma();
      String originalPostag = getAnalyzedToken(patternTokens[lemmaFrom - 1], lemmaSelect).getPOSTag();
      String desiredPostag = getAnalyzedToken(patternTokens[postagFrom - 1], postagSelect).getPOSTag();
      
      if (desiredPostag == null) {
        throw new IllegalArgumentException("AdvancedSynthesizerFilter: undefined POS tag for rule " +
          match.getRule().getFullId() + " with POS regex '" + postagSelect + "' for token: " + patternTokens[postagFrom-1]);
      }
  
      if (postagReplace != null) {
        desiredPostag = getCompositePostag(lemmaSelect, postagSelect, originalPostag, desiredPostag, postagReplace);
      }
  
      // take capitalization from the lemma (?)
      boolean isWordCapitalized = StringTools.isCapitalizedWord(patternTokens[lemmaFrom - 1].getToken());
      boolean isWordAllupper = StringTools.isAllUppercase(patternTokens[lemmaFrom - 1].getToken());
      AnalyzedToken token = new AnalyzedToken("", desiredPostag, desiredLemma);
      String[] replacements = getSynthesizer().synthesize(token, desiredPostag, true);
      
      if (replacements.length > 0) {
        RuleMatch newMatch = new RuleMatch(match.getRule(), match.getSentence(), match.getFromPos(), match.getToPos(),
            match.getMessage(), match.getShortMessage());
        newMatch.setType(match.getType());
        List<String> replacementsList = new ArrayList<>();
  
        boolean suggestionUsed = false;
        for (String r : match.getSuggestedReplacements()) {
          for (String nr : replacements) {
            if (isSuggestionException(nr, desiredPostag)) {
              continue;
            }
            if (r.contains("{suggestion}") || r.contains("{Suggestion}") || r.contains("{SUGGESTION}")) {
              suggestionUsed = true;
            }
            if (isWordCapitalized) {
              nr = StringTools.uppercaseFirstChar(nr);
            }
            if (isWordAllupper) {
              nr = nr.toUpperCase();
            }
            String completeSuggestion = r.replace("{suggestion}", nr);
            completeSuggestion = completeSuggestion.replace("{Suggestion}", StringTools.uppercaseFirstChar(nr));
            completeSuggestion = completeSuggestion.replace("{SUGGESTION}", nr.toUpperCase());
            if (!replacementsList.contains(completeSuggestion)) {
              replacementsList.add(completeSuggestion);
            }
          }
        }
        if (!suggestionUsed) {
          replacementsList.addAll(Arrays.asList(replacements));
        }
        
        List<String> adjustedReplacementsList = new ArrayList<>();
        Rule rule = match.getRule();
        if (rule instanceof AbstractPatternRule) {  
          Language lang = ((AbstractPatternRule) rule).getLanguage();
          for (String replacement : replacementsList) {
            adjustedReplacementsList.add(lang.adaptSuggestion(replacement));  
          }
        } else {
          adjustedReplacementsList = replacementsList;
        }
        newMatch.setSuggestedReplacements(adjustedReplacementsList);
        return newMatch;
      }
      return match;
    }
  
    private String getCompositePostag(String lemmaSelect, String postagSelect, String originalPostag,
        String desiredPostag, String postagReplace) {
      Pattern aPattern = Pattern.compile(lemmaSelect, Pattern.UNICODE_CASE);
      Pattern bPattern = Pattern.compile(postagSelect, Pattern.UNICODE_CASE);
      Matcher aMatcher = aPattern.matcher(originalPostag);
      Matcher bMatcher = bPattern.matcher(desiredPostag);
      String result = postagReplace;
      if (aMatcher.matches() && bMatcher.matches()) {
        for (int i = 1; i <= aMatcher.groupCount(); i++) {
          String groupStr = aMatcher.group(i);
          String toReplace = "\\\\a" + i;
          result = result.replaceAll(toReplace, groupStr);
        }
        for (int i = 1; i <= bMatcher.groupCount(); i++) {
          String groupStr = bMatcher.group(i);
          String toReplace = "\\\\b" + i;
          result = result.replaceAll(toReplace, groupStr);
        }
      }
      return result;
    }
  
    protected boolean isSuggestionException(String token, String desiredPostag) {
      return false;
    }
  
    private AnalyzedToken getAnalyzedToken(AnalyzedTokenReadings aToken, String regexp) {
      Pattern pattern = Pattern.compile(regexp);
      for (AnalyzedToken analyzedToken : aToken) {
        String posTag = analyzedToken.getPOSTag();
        if (posTag == null) {
          posTag = "UNKNOWN";
        }
        final Matcher m = pattern.matcher(posTag);
        if (m.matches()) {
          return analyzedToken;
        }
      }
      // Return the first one. Something is wrong, anyway
      return aToken.getAnalyzedToken(0);
    }
  
  }
  