in languagetool-core/src/main/java/org/languagetool/rules/patterns/AbstractPatternRulePerformer.java [169:268]
protected boolean testAllReadings(AnalyzedTokenReadings[] tokens,
PatternTokenMatcher matcher, PatternTokenMatcher prevElement,
int tokenNo, int firstMatchToken, int prevSkipNext)
throws IOException {
boolean anyMatched = false;
int numberOfReadings = tokens[tokenNo].getReadingsLength();
matcher.prepareAndGroup(firstMatchToken, tokens, rule.getLanguage());
List<AnalyzedToken> readingsToUnify = toUnify == null ? null : new ArrayList<>();
for (int i = 0; i < numberOfReadings; i++) {
AnalyzedToken matchToken = tokens[tokenNo].getAnalyzedToken(i);
prevMatched = prevMatched || prevSkipNext > 0
&& prevElement != null
&& prevElement.isMatchedByScopeNextException(matchToken);
// a workaround to allow exception with scope="next" without "skip" in previous token
// this allows to check for exception in the next token even if the current one is the last one in the sentence
prevMatched = prevMatched || prevSkipNext == 0
&& tokenNo <= tokens.length-2
&& matcher.isMatchedByScopeNextException(tokens[tokenNo+1].getAnalyzedToken(0));
if (prevMatched) {
return false;
}
boolean readingTested = false;
boolean readingMatches = false;
if (!anyMatched) {
anyMatched = readingMatches = matcher.isMatched(matchToken);
readingTested = true;
}
//short-circuit when the search cannot possibly match
if (!anyMatched && (prevElement == null || !prevElement.getPatternToken().hasCurrentOrNextExceptions())) {
if (matcher.getPatternToken().getPOStag() == null) {
if (matcher.getPatternToken().isInflected()) {
if (tokens[tokenNo].hasSameLemmas()) {
return false; // same lemmas everywhere
}
} else {
return false; // the token is the same, we will not get a match
}
} else if (!matcher.getPatternToken().getPOSNegation() // postag =! null
&& !tokens[tokenNo].isTagged()) {
return false; // we won't find any postag here anyway
}
}
if (rule.isGroupsOrUnification()) {
if (!readingTested) {
readingMatches = matcher.isMatched(matchToken);
}
boolean isLastReading = i + 1 == numberOfReadings;
PatternToken elem = matcher.getPatternToken();
if (readingMatches && readingsToUnify != null && elem.isUnified() && !elem.isUnificationNeutral()) {
readingsToUnify.add(matchToken);
}
anyMatched &= testAndGroup(isLastReading, matchToken, matcher);
}
}
if (anyMatched) {
for (int i = 0; i < numberOfReadings; i++) {
if (matcher.isExceptionMatchedCompletely(tokens[tokenNo].getAnalyzedToken(i))) {
return false;
}
}
if (tokenNo > 0 && matcher.hasPreviousException()) {
if (matcher.isMatchedByPreviousException(tokens[tokenNo - 1])) {
return false;
}
}
if (matcher.getPatternToken().isUnificationNeutral() && neutralReadings != null) {
neutralReadings.computeIfAbsent(matcher.getPatternToken(), __ -> new ArrayList<>()).add(tokens[tokenNo]);
}
}
ChunkTag chunkTag = matcher.getPatternToken().getChunkTag();
if (chunkTag != null) {
if (chunkTag.isRegexp()) {
anyMatched &= tokens[tokenNo].getChunkTags().stream().anyMatch(k -> k.getChunkTag().matches(chunkTag.getChunkTag()))
^ matcher.getPatternToken().getNegation();
} else {
anyMatched &= tokens[tokenNo].getChunkTags().contains(chunkTag)
^ matcher.getPatternToken().getNegation();
}
}
if (matcher.getPatternToken().hasAndGroup()) {
for (PatternToken e : matcher.getPatternToken().getAndGroup()) {
if (e.getChunkTag() != null) {
anyMatched &= tokens[tokenNo].getChunkTags().contains(e.getChunkTag())
^ e.getNegation();
}
}
}
if (anyMatched && readingsToUnify != null && !readingsToUnify.isEmpty()) {
toUnify.computeIfAbsent(matcher.getPatternToken(), __ -> new ArrayList<>()).add(readingsToUnify);
}
return anyMatched;
}