blob: 0283753e2c3c7be4ae40675c91d7d3e45ee12c73 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* A pattern that matches against filesystem path-like strings with wildcards.
*
* The pattern matches strings as follows:
* * The pattern must use `/` as the path separator.
* * The whole string must match, not a substring.
* * Any non wildcard is matched as a literal.
* * '*' matches one or more characters except '/'.
* * '?' matches exactly one character except '/'.
* * '**' matches one or more characters including '/'.
*/
class Glob {
/**
* The special characters are: \ ^ $ . | + [ ] ( ) { }
* as defined here: http://ecma-international.org/ecma-262/5.1/#sec-15.10
*/
static final RegExp _specialChars =
new RegExp(r'([\\\^\$\.\|\+\[\]\(\)\{\}])');
/**
* The path separator used to separate components in file paths.
*/
final String _separator;
/**
* The pattern string.
*/
final String _pattern;
String _suffix;
RegExp _regex;
Glob(this._separator, this._pattern) {
if (_hasJustPrefix(_pattern, '**/*')) {
_suffix = _pattern.substring(4).toLowerCase();
} else if (_hasJustPrefix(_pattern, '**')) {
_suffix = _pattern.substring(2).toLowerCase();
} else {
_regex = _regexpFromGlobPattern(_pattern);
}
}
@override
int get hashCode => _pattern.hashCode;
bool operator ==(other) => other is Glob && _pattern == other._pattern;
/**
* Return `true` if the given [path] matches this glob.
* The given [path] must use the same [_separator] as the glob.
*/
bool matches(String path) {
String posixPath = _toPosixPath(path);
if (_suffix != null) {
return posixPath.toLowerCase().endsWith(_suffix);
}
return _regex.matchAsPrefix(posixPath) != null;
}
@override
String toString() => _pattern;
/**
* Return the Posix version of the given [path].
*/
String _toPosixPath(String path) {
if (_separator == '/') {
return path;
}
return path.replaceAll(_separator, '/');
}
/**
* Return `true` if the [pattern] start with the given [prefix] and does
* not have `*` or `?` characters after the [prefix].
*/
static bool _hasJustPrefix(String pattern, String prefix) {
if (pattern.startsWith(prefix)) {
int prefixLength = prefix.length;
return !pattern.contains('*', prefixLength) &&
!pattern.contains('?', prefixLength);
}
return false;
}
static RegExp _regexpFromGlobPattern(String pattern) {
StringBuffer sb = new StringBuffer();
sb.write('^');
List<String> chars = pattern.split('');
for (int i = 0; i < chars.length; i++) {
String c = chars[i];
if (_specialChars.hasMatch(c)) {
sb.write(r'\');
sb.write(c);
} else if (c == '*') {
if (i + 1 < chars.length && chars[i + 1] == '*') {
sb.write('.*');
i++;
} else {
sb.write('[^/]*');
}
} else if (c == '?') {
sb.write('[^/]');
} else {
sb.write(c);
}
}
sb.write(r'$');
return new RegExp(sb.toString(), caseSensitive: false);
}
}