class PartialPosTagFilter extends RuleFilter {

  @Nullable
  protected abstract List<AnalyzedTokenReadings> tag(String token);

  @Override
  public RuleMatch acceptRuleMatch(RuleMatch match, Map<String, String> args, int patternTokenPos, AnalyzedTokenReadings[] patternTokens, List<Integer> tokenPositions) {<FILL_FUNCTION_BODY>}

  private boolean partialTagHasRequiredTag(List<AnalyzedTokenReadings> tags, String requiredTagRegexp, boolean negatePos) {
    // Without negate_pos=yes: return true if any postag matches the regexp.
    // With negate_pos=yes:    return true if there are postag and none them matches the regexp.
    int postagCount = 0;
    for (AnalyzedTokenReadings tag : tags) {
      for (AnalyzedToken analyzedToken : tag.getReadings()) {
        if (analyzedToken.getPOSTag() != null) {
          if (negatePos) {
            postagCount++;
            if (analyzedToken.getPOSTag().matches(requiredTagRegexp)) {
              return false;
            }
          } else {
            if (analyzedToken.getPOSTag().matches(requiredTagRegexp)) {
              return true;
            }
          }
        }
      }
    }
    return postagCount == 0 ? false : negatePos;
  }
}

    if (!(args.containsKey("no") && args.containsKey("regexp") && args.containsKey("postag_regexp"))) {
      throw new RuntimeException("Set 'no', 'regexp' and 'postag_regexp' for filter " + PartialPosTagFilter.class.getSimpleName());
    }
    int tokenPos = Integer.parseInt(args.get("no"));
    Pattern pattern = Pattern.compile(args.get("regexp"));
    String requiredTagRegexp = args.get("postag_regexp");
    boolean negatePos = args.containsKey("negate_pos");
    boolean two_groups_regexp = args.containsKey("two_groups_regexp");

      String prefix = "";
      String suffix = "";

      if (args.containsKey("prefix")) {
          prefix = args.get("prefix");
      };

      if (args.containsKey("suffix")) {
          suffix = args.get("suffix");
      };

     
    String token = prefix + patternTokens[tokenPos - 1].getToken() + suffix;
    
    Matcher matcher = pattern.matcher(token);
    if ((matcher.groupCount() != 1) && !(two_groups_regexp)) {
      throw new RuntimeException("Got " + matcher.groupCount() + " groups for regex '" + pattern.pattern() + "', expected 1");
    }
    if ((matcher.groupCount() != 2) && (two_groups_regexp)) {
      throw new RuntimeException("Got " + matcher.groupCount() + " groups for regex '" + pattern.pattern() + "', expected 2");
    }
    if (matcher.matches()) {
      String partialToken = matcher.group(1);
      if (matcher.groupCount() == 2) {
        partialToken += matcher.group(2);
      } 
      List<AnalyzedTokenReadings> tags = tag(partialToken);
      if (tags != null && partialTagHasRequiredTag(tags, requiredTagRegexp, negatePos)) {
        return match;
      }
      return null;
    }
    return null;
  
<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>