package org.smoothbuild.builtin.file.match;
import static org.smoothbuild.builtin.file.match.Constants.SINGLE_STAR;
import java.util.List;
import java.util.function.Predicate;
import org.smoothbuild.io.fs.base.Path;
/**
* Matches file/dir name against given pattern.
*
* <pre>
* Each star '*' in pattern matches zero or more characters.
* Double stars "**" are forbidden.
* </pre>
*/
public class NameMatcher implements Predicate<Path> {
private final List<String> patternParts;
public NameMatcher(NamePattern pattern) {
if (pattern.isDoubleStar()) {
throw new IllegalArgumentException();
}
this.patternParts = pattern.parts();
}
public boolean test(Path path) {
String string = path.value();
// matching leftmost characters to first '*'
int pLeft = 0;
int sLeft = 0;
while (pLeft < patternParts.size()) {
String part = patternParts.get(pLeft);
if (part.equals(SINGLE_STAR)) {
break;
} else if (string.substring(sLeft).startsWith(part)) {
sLeft += part.length();
pLeft++;
} else {
return false;
}
}
if (pLeft == patternParts.size()) {
return sLeft == string.length();
}
// matching rightmost characters to last '*'
int pRight = patternParts.size() - 1;
int sRight = string.length();
while (pLeft < pRight) {
String part = patternParts.get(pRight);
if (part.equals(SINGLE_STAR)) {
break;
} else if (string.substring(0, sRight).endsWith(part)) {
sRight -= part.length();
pRight--;
} else {
return false;
}
}
/*
* At this point pLeft and pRight points to parts that contain SINGLE_STAR.
* If they both point to the same part then we've matched the whole pattern
* - matching is successful if sRight and sLeft haven't overlapped. All
* these cases are checked inside 'while' clause below.
*/
int stillToMatch = minimumCharactersNeeded(pLeft + 1, pRight - 1);
int stillAvailable = sRight - sLeft;
while (true) {
if (stillAvailable < stillToMatch) {
return false;
}
if (pLeft == pRight) {
return true;
}
pLeft++;
String part = patternParts.get(pLeft);
int steps = stillAvailable - part.length() + 1;
boolean found = false;
for (int i = 0; i < steps; i++) {
if (string.substring(sLeft + i).startsWith(part)) {
pLeft++;
sLeft += part.length() + i;
found = true;
break;
}
}
if (!found) {
return false;
}
}
}
private int minimumCharactersNeeded(int pLeft, int pRight) {
int result = 0;
for (int i = pLeft; i <= pRight; i++) {
String part = patternParts.get(i);
result += part.equals(SINGLE_STAR) ? 0 : part.length();
}
return result;
}
}