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

ljacqu / wordeval / 14603510580

22 Apr 2025 07:59PM UTC coverage: 60.152% (+0.3%) from 59.867%
14603510580

push

github

ljacqu
Hunspell: Parse needaffix class and continuation classes

344 of 676 branches covered (50.89%)

67 of 93 new or added lines in 5 files covered. (72.04%)

1 existing line in 1 file now uncovered.

951 of 1581 relevant lines covered (60.15%)

3.37 hits per line

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

78.72
/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+)$");
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✔
NEW
49
      } else if (line.startsWith("NEEDAFFIX ")) {
×
NEW
50
        result.setNeedAffixFlag(line.substring("NEEDAFFIX ".length()));
×
51
      } else {
NEW
52
        handleUnknownLine(line);
×
53
      }
54
    });
1✔
55

56
    return result;
2✔
57
  }
58

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

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

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

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

82
  private void handleUnknownLine(String line) {
NEW
83
    if (StringUtils.startsWithAny(line, "REP ", "MAP ", "TRY ", "WORDCHARS ", "ICONV ",
×
84
        "OCONV ", "KEY ", "BREAK ", "CHECKSHARPS", "NOSUGGEST ")) {
85
      // Nothing to do: command is not relevant for this application
NEW
86
      return;
×
NEW
87
    } else if (line.startsWith("SET ")) {
×
NEW
88
      if (!line.startsWith("SET UTF-8")) {
×
NEW
89
        log.warn("Found unexpected encoding directive: {}", line);
×
90
      }
91
    } else {
NEW
92
      log.info("Unknown line: {}", line);
×
93
    }
NEW
94
  }
×
95
}
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