• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ljacqu / wordeval / 14695353888

27 Apr 2025 07:21PM UTC coverage: 61.437% (+0.6%) from 60.848%
14695353888

push

github

ljacqu
Support comments in affix class declarations

385 of 694 branches covered (55.48%)

1 of 1 new or added line in 1 file covered. (100.0%)

6 existing lines in 2 files now uncovered.

975 of 1587 relevant lines covered (61.44%)

3.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.88
/src/main/java/ch/jalu/wordeval/dictionary/hunspell/parser/AffixesParser.java
1
package ch.jalu.wordeval.dictionary.hunspell.parser;
2

3
import ch.jalu.wordeval.dictionary.hunspell.AffixFlagType;
4
import ch.jalu.wordeval.dictionary.hunspell.AffixType;
5
import lombok.extern.slf4j.Slf4j;
6
import org.apache.commons.lang3.StringUtils;
7
import org.springframework.stereotype.Component;
8

9
import java.util.regex.Matcher;
10
import java.util.regex.Pattern;
11
import java.util.stream.Stream;
12

13
/**
14
 * Hunspell .aff parser.
15
 */
16
@Slf4j
3✔
17
@Component
18
public class AffixesParser {
3✔
19

20
  // useful documentation: https://linux.die.net/man/4/hunspell
21

22
  // e.g. SFX V N 2
23
  private static final Pattern AFFIX_CLASS_HEADER_PATTERN =
1✔
24
      Pattern.compile("^(PFX|SFX)\\s+(\\S+)\\s+([YN])\\s+(\\d+)(\\s?#.*?)?$");
2✔
25
  // e.g. SFX V   e  ive  e
26
  private static final Pattern AFFIX_RULE_PATTERN =
2✔
27
      Pattern.compile("^(PFX|SFX)\\s+\\S+\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)(.*?)?$");
2✔
28

29
  public ParsedAffixes parseAffFile(Stream<String> lines) {
30
    ParsedAffixes result = new ParsedAffixes();
4✔
31

32
    lines.forEach(line -> {
5✔
33
      line = line.trim();
3✔
34
      if (line.isEmpty() || line.startsWith("#")) {
7✔
35
        return;
1✔
36
      }
37

38
      Matcher headerMatcher = AFFIX_CLASS_HEADER_PATTERN.matcher(line);
4✔
39
      Matcher ruleMatcher = AFFIX_RULE_PATTERN.matcher(line);
4✔
40

41
      if (headerMatcher.matches()) {
3✔
42
        ParsedAffixClass affixClass = mapAffixClass(headerMatcher);
3✔
43
        result.addAffixClass(affixClass);
3✔
44
      } else if (ruleMatcher.matches()) {
4✔
45
        ParsedAffixClass.Rule rule = mapAffixRule(ruleMatcher, result.getFlagType());
5✔
46
        result.addRuleToCurrentClass(rule);
3✔
47
      } else if (line.startsWith("FLAG ")) {
5✔
48
        result.setFlagType(AffixFlagType.fromAffixFileString(line.substring("FLAG ".length())));
8✔
49
      } else if (line.startsWith("NEEDAFFIX ")) {
4✔
50
        result.setNeedAffixFlag(line.substring("NEEDAFFIX ".length()));
7✔
51
      } else if (line.startsWith("FORBIDDENWORD ")) {
4✔
52
        result.setForbiddenWordClass(line.substring("FORBIDDENWORD ".length()));
7✔
53
      } else {
54
        handleUnknownLine(line);
3✔
55
      }
56
    });
1✔
57

58
    return result;
2✔
59
  }
60

61
  private static ParsedAffixClass mapAffixClass(Matcher headerMatcher) {
62
    ParsedAffixClass affixClass = new ParsedAffixClass();
4✔
63
    affixClass.type = AffixType.fromString(headerMatcher.group(1));
6✔
64
    affixClass.flag = headerMatcher.group(2);
5✔
65
    affixClass.crossProduct = headerMatcher.group(3).equalsIgnoreCase("Y");
7✔
66
    return affixClass;
2✔
67
  }
68

69
  private static ParsedAffixClass.Rule mapAffixRule(Matcher ruleMatcher, AffixFlagType flagType) {
70
    String strip = ruleMatcher.group(2).equals("0") ? "" : ruleMatcher.group(2);
12✔
71
    String affix = ruleMatcher.group(3).equals("0") ? "" : ruleMatcher.group(3);
10!
72
    String condition = ruleMatcher.group(4);
4✔
73

74
    int slashIndex = affix.indexOf('/');
4✔
75
    if (slashIndex < 0) {
2✔
76
      return new ParsedAffixClass.Rule(strip, affix, condition);
7✔
77
    }
78

79
    String continuationClasses = affix.substring(slashIndex + 1);
6✔
80
    affix = affix.substring(0, slashIndex);
5✔
81
    return new ParsedAffixClass.Rule(strip, affix, flagType.split(continuationClasses), condition);
10✔
82
  }
83

84
  private void handleUnknownLine(String line) {
85
    if (StringUtils.startsWithAny(line, "REP ", "MAP ", "TRY ", "WORDCHARS ", "ICONV ",
45!
86
        "OCONV ", "KEY ", "BREAK ", "CHECKSHARPS", "NOSUGGEST ")) {
87
      // Nothing to do: command is not relevant for this application
88
      return;
×
89
    } else if (line.startsWith("SET ")) {
4!
90
      if (!line.startsWith("SET UTF-8")) {
4!
UNCOV
91
        log.warn("Found unexpected encoding directive: {}", line);
×
92
      }
93
    } else {
94
      log.info("Unknown line: {}", line);
×
95
    }
96
  }
1✔
97
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc