Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
305 changes: 153 additions & 152 deletions src/main/java/com/github/dockerjava/core/GoLangFileMatch.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

Expand Down Expand Up @@ -49,6 +50,8 @@ private GoLangFileMatch() {

public static final boolean IS_WINDOWS = File.separatorChar == '\\';

private static final String PATTERN_CHARS_TO_ESCAPE = "\\.[]{}()*+-?^$|";

public static boolean match(List<String> patterns, File file) {
return !match(patterns, file.getPath()).isEmpty();
}
Expand All @@ -71,196 +74,194 @@ public static List<String> match(List<String> patterns, String name) {
}

public static boolean match(String pattern, String name) {
Pattern: while (!pattern.isEmpty()) {
ScanResult scanResult = scanChunk(pattern);
pattern = scanResult.pattern;
if (scanResult.star && StringUtils.isEmpty(scanResult.chunk)) {
// Trailing * matches rest of string unless it has a /.
return name.indexOf(File.separatorChar) < 0;
}
// Look for match at current position.
String matchResult = matchChunk(scanResult.chunk, name);
return buildPattern(pattern).matcher(name).matches();
}

// if we're the last chunk, make sure we've exhausted the name
// otherwise we'll give a false result even if we could still match
// using the star
if (matchResult != null && (matchResult.isEmpty() || !pattern.isEmpty())) {
name = matchResult;
continue;
}
if (scanResult.star) {
for (int i = 0; i < name.length() && name.charAt(i) != File.separatorChar; i++) {
matchResult = matchChunk(scanResult.chunk, name.substring(i + 1));
if (matchResult != null) {
// if we're the last chunk, make sure we exhausted the name
if (pattern.isEmpty() && !matchResult.isEmpty()) {
continue;
}
name = matchResult;
continue Pattern;
}
}
private static Pattern buildPattern(String pattern) {
StringBuilder patternStringBuilder = new StringBuilder("^");
while (!pattern.isEmpty()) {
pattern = appendChunkPattern(patternStringBuilder, pattern);

if (!pattern.isEmpty()) {
patternStringBuilder.append(quote(File.separatorChar));
}
return false;
}
return name.isEmpty();
patternStringBuilder.append("(").append(quote(File.separatorChar)).append(".*").append(")?");
return Pattern.compile(patternStringBuilder.toString());
}

static ScanResult scanChunk(String pattern) {
boolean star = false;
if (!pattern.isEmpty() && pattern.charAt(0) == '*') {
pattern = pattern.substring(1);
star = true;
private static String quote(char separatorChar) {
if (StringUtils.contains(PATTERN_CHARS_TO_ESCAPE, separatorChar)) {
return "\\" + separatorChar;
} else {
return String.valueOf(separatorChar);
}
}

private static String appendChunkPattern(StringBuilder patternStringBuilder, String pattern) {
if (pattern.equals("**") || pattern.startsWith("**" + File.separator)) {
patternStringBuilder.append("(")
.append("[^").append(quote(File.separatorChar)).append("]*")
.append("(")
.append(quote(File.separatorChar)).append("[^").append(quote(File.separatorChar)).append("]*")
.append(")*").append(")?");
return pattern.substring(pattern.length() == 2 ? 2 : 3);
}

boolean inRange = false;
int rangeFrom = 0;
RangeParseState rangeParseState = RangeParseState.CHAR_EXPECTED;
boolean isEsc = false;
int i;
Scan: for (i = 0; i < pattern.length(); i++) {
switch (pattern.charAt(i)) {
case '\\': {
if (!IS_WINDOWS && i + 1 < pattern.length()) {
i++;
for (i = 0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
switch (c) {
case '/':
if (!inRange) {
if (!IS_WINDOWS && !isEsc) {
// end of chunk
return pattern.substring(i + 1);
} else {
patternStringBuilder.append(quote(c));
}
} else {
rangeParseState = nextStateAfterChar(rangeParseState);
}
isEsc = false;
break;
}
case '[':
inRange = true;
break;
case ']':
inRange = false;
break;
case '*':
case '\\':
if (!inRange) {
break Scan;
if (!IS_WINDOWS) {
if (isEsc) {
patternStringBuilder.append(quote(c));
isEsc = false;
} else {
isEsc = true;
}
} else {
// end of chunk
return pattern.substring(i + 1);
}
} else {
if (IS_WINDOWS || isEsc) {
rangeParseState = nextStateAfterChar(rangeParseState);
isEsc = false;
} else {
isEsc = true;
}
}
}
}
return new ScanResult(star, pattern.substring(0, i), pattern.substring(i));
}

static String matchChunk(String chunk, String s) {
int chunkLength = chunk.length();
int chunkOffset = 0;
int sLength = s.length();
int sOffset = 0;
char r;
while (chunkOffset < chunkLength) {
if (sOffset == sLength) {
return null;
}
switch (chunk.charAt(chunkOffset)) {
break;
case '[':
r = s.charAt(sOffset);
sOffset++;
chunkOffset++;
// We can't end right after '[', we're expecting at least
// a closing bracket and possibly a caret.
if (chunkOffset == chunkLength) {
throw new GoLangFileMatchException();
}
// possibly negated
boolean negated = chunk.charAt(chunkOffset) == '^';
if (negated) {
chunkOffset++;
if (!isEsc) {
if (inRange) {
//"[ is not expected, ] had not reached"
throw new GoLangFileMatchException();
}
rangeFrom = i;
rangeParseState = RangeParseState.CHAR_EXPECTED;
inRange = true;
} else {
if (!inRange) {
patternStringBuilder.append(c);
} else {
rangeParseState = nextStateAfterChar(rangeParseState);
}
}
// parse all ranges
boolean match = false;
int nrange = 0;
while (true) {
if (chunkOffset < chunkLength && chunk.charAt(chunkOffset) == ']' && nrange > 0) {
chunkOffset++;
break;
isEsc = false;
break;
case ']':
if (!isEsc) {
if (!inRange) {
//"] is not expected, [ was not met"
throw new GoLangFileMatchException();
}
GetEscResult result = getEsc(chunk, chunkOffset, chunkLength);
char lo = result.lo;
char hi = lo;
chunkOffset = result.chunkOffset;
if (chunk.charAt(chunkOffset) == '-') {
result = getEsc(chunk, ++chunkOffset, chunkLength);
chunkOffset = result.chunkOffset;
hi = result.lo;
if (rangeParseState == RangeParseState.CHAR_EXPECTED_AFTER_DASH) {
// character range not finished
throw new GoLangFileMatchException();
}
if (lo <= r && r <= hi) {
match = true;
patternStringBuilder.append(pattern.substring(rangeFrom, i + 1));
inRange = false;
} else {
if (!inRange) {
patternStringBuilder.append(c);
} else {
rangeParseState = nextStateAfterChar(rangeParseState);
}
nrange++;
}
if (match == negated) {
return null;
isEsc = false;
break;
case '*':
if (!inRange) {
if (!isEsc) {
patternStringBuilder.append("[^").append(quote(File.separatorChar)).append("]*");
} else {
patternStringBuilder.append(quote(c));
}
} else {
rangeParseState = nextStateAfterChar(rangeParseState);
}
isEsc = false;
break;

case '?':
if (s.charAt(sOffset) == File.separatorChar) {
return null;
if (!inRange) {
if (!isEsc) {
patternStringBuilder.append("[^").append(quote(File.separatorChar)).append("]");
} else {
patternStringBuilder.append(quote(c));
}
} else {
rangeParseState = nextStateAfterChar(rangeParseState);
}
sOffset++;
chunkOffset++;
isEsc = false;
break;
case '\\':
if (!IS_WINDOWS) {
chunkOffset++;
if (chunkOffset == chunkLength) {
throw new GoLangFileMatchException();
case '-':
if (!inRange) {
patternStringBuilder.append(quote(c));
} else {
if (!isEsc) {
if (rangeParseState != RangeParseState.CHAR_OR_DASH_EXPECTED) {
// - not expected
throw new GoLangFileMatchException();
}
rangeParseState = RangeParseState.CHAR_EXPECTED_AFTER_DASH;
} else {
rangeParseState = nextStateAfterChar(rangeParseState);
}
}
// fallthrough
isEsc = false;
break;
default:
if (chunk.charAt(chunkOffset) != s.charAt(sOffset)) {
return null;
if (!inRange) {
patternStringBuilder.append(quote(c));
} else {
rangeParseState = nextStateAfterChar(rangeParseState);
}
sOffset++;
chunkOffset++;
isEsc = false;
}
}
return s.substring(sOffset);
}

static GetEscResult getEsc(String chunk, int chunkOffset, int chunkLength) {
if (chunkOffset == chunkLength) {
if (isEsc) {
// "Escaped character missing"
throw new GoLangFileMatchException();
}
char r = chunk.charAt(chunkOffset);
if (r == '-' || r == ']') {
if (inRange) {
// "Character range not finished"
throw new GoLangFileMatchException();
}
if (r == '\\' && !IS_WINDOWS) {
chunkOffset++;
if (chunkOffset == chunkLength) {
throw new GoLangFileMatchException();
}

}
r = chunk.charAt(chunkOffset);
chunkOffset++;
if (chunkOffset == chunkLength) {
throw new GoLangFileMatchException();
}
return new GetEscResult(r, chunkOffset);
return "";
}

private static final class ScanResult {
public boolean star;

public String chunk;

public String pattern;

ScanResult(boolean star, String chunk, String pattern) {
this.star = star;
this.chunk = chunk;
this.pattern = pattern;
private static RangeParseState nextStateAfterChar(RangeParseState currentState) {
if (currentState == RangeParseState.CHAR_EXPECTED_AFTER_DASH) {
return RangeParseState.CHAR_EXPECTED;
} else {
return RangeParseState.CHAR_OR_DASH_EXPECTED;
}
}

private static final class GetEscResult {
public char lo;

public int chunkOffset;

GetEscResult(char lo, int chunkOffset) {
this.lo = lo;
this.chunkOffset = chunkOffset;
}
private enum RangeParseState {
CHAR_EXPECTED,
CHAR_OR_DASH_EXPECTED,
CHAR_EXPECTED_AFTER_DASH
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,10 @@ private List<String> matchingIgnorePatterns(String fileName) {

int lineNumber = 0;
for (String pattern : ignores) {
String goLangPattern = pattern.startsWith("!") ? pattern.substring(1) : pattern;
lineNumber++;
try {
if (GoLangFileMatch.match(pattern, fileName)) {
if (GoLangFileMatch.match(goLangPattern, fileName)) {
matches.add(pattern);
}
} catch (GoLangFileMatchException e) {
Expand All @@ -233,21 +234,7 @@ private String effectiveMatchingIgnorePattern(File file) {

String lastMatchingPattern = matchingPattern.get(matchingPattern.size() - 1);

int lastMatchingPatternIndex = ignores.lastIndexOf(lastMatchingPattern);

if (lastMatchingPatternIndex == ignores.size() - 1) {
return lastMatchingPattern;
}

List<String> remainingIgnorePattern = ignores.subList(lastMatchingPatternIndex + 1, ignores.size());

for (String ignorePattern : remainingIgnorePattern) {
if (ignorePattern.equals("!" + relativeFilename)) {
return null;
}
}

return lastMatchingPattern;
return !lastMatchingPattern.startsWith("!") ? lastMatchingPattern : null;
}
}
}
Loading