class PolishSynthesizer extends BaseSynthesizer implements Synthesizer {

  private static final String RESOURCE_FILENAME = "/pl/polish_synth.dict";
  private static final String TAGS_FILE_NAME = "/pl/polish_tags.txt";

  private static final String POTENTIAL_NEGATION_TAG = ":aff";
  private static final String NEGATION_TAG = ":neg";
  private static final Pattern NEGATION_TAG_PATTERN = Pattern.compile(NEGATION_TAG);
  private static final String COMP_TAG = "com";
  private static final String SUP_TAG = "sup";
  private static final Pattern PATTERN = Pattern.compile(".*[a-z]\\.[a-z].*");
  private static final Pattern DOT = Pattern.compile(".", Pattern.LITERAL);

  private List<String> possibleTags;

  public static final PolishSynthesizer INSTANCE = new PolishSynthesizer();

  /** @deprecated use {@link #INSTANCE} */
  public PolishSynthesizer(Language lang) {
    this();
  }
  private PolishSynthesizer() {
    super(RESOURCE_FILENAME, TAGS_FILE_NAME, "pl");
  }

  @Override
  public final String[] synthesize(AnalyzedToken token, String posTag) throws IOException {
    if (posTag == null) {
      return null;
    }
    IStemmer synthesizer = new DictionaryLookup(getDictionary());
    boolean isNegated = false;
    if (token.getPOSTag() != null) {
      isNegated = posTag.indexOf(NEGATION_TAG) > 0
          || token.getPOSTag().indexOf(NEGATION_TAG) > 0
          && !(posTag.indexOf(COMP_TAG) > 0) && !(posTag.indexOf(SUP_TAG) > 0);
    }
    if (posTag.indexOf('+') > 0) {
      return synthesize(token, posTag, true);
    }
    List<String> forms = getWordForms(token, posTag, isNegated, synthesizer);
    return forms.toArray(new String[0]);
  }

  @Override
  public final String[] synthesize(AnalyzedToken token, String pos, boolean posTagRegExp) throws IOException {<FILL_FUNCTION_BODY>}

  @Override
  public final String getPosTagCorrection(String posTag) {
    if (posTag.contains(".")) {
      String[] tags = posTag.split(":");
      int pos = -1;
      for (int i = 0; i < tags.length; i++) {
        if (PATTERN.matcher(tags[i]).matches()) {
          tags[i] = "(.*" + DOT.matcher(tags[i]).replaceAll(".*|.*") + ".*)";
          pos = i;
        }
      }
      if (pos == -1) {
        return posTag;
      }
      StringBuilder sb = new StringBuilder();
      sb.append(tags[0]);
      for (int i = 1; i < tags.length; i++) {
        sb.append(':');
        sb.append(tags[i]);
      }
      return sb.toString();
    }
    return posTag;
  }

  private List<String> getWordForms(AnalyzedToken token, String posTag,
      boolean isNegated, IStemmer synthesizer) {
    List<String> forms = new ArrayList<>();
    List<WordData> wordForms;
    if (isNegated) {
      wordForms = synthesizer.lookup(token.getLemma() + "|"
          + NEGATION_TAG_PATTERN.matcher(posTag).replaceFirst(POTENTIAL_NEGATION_TAG));
      if (wordForms != null) {                      
        for (WordData wd : wordForms) {
          forms.add("nie" + wd.getStem());
        }
      }
    } else {
      wordForms = synthesizer.lookup(token.getLemma() + "|" + posTag);
      for (WordData wd : wordForms) {
        if (wd.getStem() != null) {
          forms.add(wd.getStem().toString());
        }
      }      
    }
    return forms;
  }

}

    if (pos == null) {
      return null;
    }
    String posTag = pos;
    if (posTagRegExp) {
      if (possibleTags == null) {
        try (InputStream stream = JLanguageTool.getDataBroker().getFromResourceDirAsStream(TAGS_FILE_NAME)) {
          possibleTags = SynthesizerTools.loadWords(stream);
        }
      }
      IStemmer synthesizer = new DictionaryLookup(getDictionary());
      List<String> results = new ArrayList<>();

      boolean isNegated = false;
      if (token.getPOSTag() != null) {
        isNegated = posTag.indexOf(NEGATION_TAG) > 0
            || token.getPOSTag().indexOf(NEGATION_TAG) > 0
            && !(posTag.indexOf(COMP_TAG) > 0)
            && !(posTag.indexOf(SUP_TAG) > 0);
      }

      if (isNegated) {
        posTag = posTag.replaceAll(NEGATION_TAG, POTENTIAL_NEGATION_TAG + "?");
      }

      try {
        Pattern p = Pattern.compile(posTag.replace('+', '|'));
        for (String tag : possibleTags) {
          Matcher m = p.matcher(tag);
          if (m.matches()) {
            List<String> wordForms = getWordForms(token, tag, isNegated, synthesizer);
            if (wordForms != null) {
              results.addAll(wordForms);
            }
          }
        }
      } catch (PatternSyntaxException e) {
        // catch this rare error which I couldn't fix yet (https://github.com/languagetool-org/languagetool/issues/1651):
        //java.util.regex.PatternSyntaxException: Unclosed group near index 17
        //(.*)(sg|pl.*acc.*
        //                 ^
        e.printStackTrace();
      }
      //remove duplicates
      Set<String> hs = new HashSet<>(results);
      results.clear();
      results.addAll(hs);     
      
      return results.toArray(new String[0]);
    }
    return synthesize(token, posTag);
  
<methods>public void <init>(java.lang.String, java.lang.String, java.lang.String, org.languagetool.Language) ,public void <init>(java.lang.String, java.lang.String, java.lang.String, java.lang.String) ,public void <init>(java.lang.String, java.lang.String, org.languagetool.Language) ,public void <init>(java.lang.String, java.lang.String, java.lang.String) ,public java.lang.String getPosTagCorrection(java.lang.String) ,public java.lang.String getRomanNumber(java.lang.String) ,public java.lang.String getSpelledNumber(java.lang.String) ,public IStemmer getStemmer() ,public java.lang.String getTargetPosTag(List<java.lang.String>, java.lang.String) ,public java.lang.String[] synthesize(org.languagetool.AnalyzedToken, java.lang.String) throws java.io.IOException,public java.lang.String[] synthesize(org.languagetool.AnalyzedToken, java.lang.String, boolean) throws java.io.IOException,public java.lang.String[] synthesizeForPosTags(java.lang.String, Predicate<java.lang.String>) throws java.io.IOException<variables>public final java.lang.String SPELLNUMBER_FEMININE_TAG,public final java.lang.String SPELLNUMBER_ROMAN_TAG,public final java.lang.String SPELLNUMBER_TAG,private volatile Dictionary dictionary,private final non-sealed org.languagetool.synthesis.ManualSynthesizer manualSynthesizer,private final non-sealed org.languagetool.synthesis.Soros numberSpeller,protected volatile List<java.lang.String> possibleTags,private final non-sealed org.languagetool.synthesis.ManualSynthesizer removalSynthesizer,private final non-sealed org.languagetool.synthesis.ManualSynthesizer removalSynthesizer2,private final non-sealed java.lang.String resourceFileName,private final non-sealed org.languagetool.synthesis.Soros romanNumberer,private final non-sealed java.lang.String sorosFileName,private final non-sealed IStemmer stemmer,private final non-sealed java.lang.String tagFileName