aQute.libg.glob.Glob Maven / Gradle / Ivy
package aQute.libg.glob;
import java.io.File;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Glob {
enum State {
SIMPLE,
CURLIES,
BRACKETS,
QUOTED
}
public static final Glob ALL = new Glob("*");
public static final Glob NONE = new Glob("\uFFFF\uFFFB\uFFFE\uFFFD");
private final String glob;
private final Pattern pattern;
public Glob(String globString) {
this(globString, 0);
}
public Glob(String globString, int flags) {
this(globString, toPattern(globString, flags));
}
protected Glob(String globString, Pattern pattern) {
this.glob = globString;
this.pattern = pattern;
}
public String glob() {
return glob;
}
public Pattern pattern() {
return pattern;
}
public Matcher matcher(CharSequence input) {
return pattern.matcher(input);
}
@Override
public String toString() {
return glob;
}
public static Pattern toPattern(String line) {
return toPattern(line, 0);
}
public static Pattern toPattern(String line, int flags) {
line = line.trim();
int strLen = line.length();
StringBuilder sb = new StringBuilder(strLen << 2);
int curlyLevel = 0;
State state = State.SIMPLE;
char previousChar = 0;
for (int i = 0; i < strLen; i++) {
char currentChar = line.charAt(i);
switch (currentChar) {
case '*' :
if ((state == State.SIMPLE || state == State.CURLIES) && !isEnd(previousChar)) {
sb.append('.');
}
sb.append(currentChar);
break;
case '?' :
if ((state == State.SIMPLE || state == State.CURLIES) && !isStart(previousChar)
&& !isEnd(previousChar)) {
sb.append('.');
} else {
sb.append(currentChar);
}
break;
case '+' :
if ((state == State.SIMPLE || state == State.CURLIES) && !isEnd(previousChar)) {
sb.append('\\');
}
sb.append(currentChar);
break;
case '\\' :
sb.append(currentChar);
if (i + 1 < strLen) {
char nextChar = line.charAt(++i);
if (state == State.SIMPLE && nextChar == 'Q') {
state = State.QUOTED;
} else if (state == State.QUOTED && nextChar == 'E') {
state = State.SIMPLE;
}
sb.append(nextChar);
}
break;
case '[' :
if (state == State.SIMPLE) {
state = State.BRACKETS;
}
sb.append(currentChar);
break;
case ']' :
if (state == State.BRACKETS) {
state = State.SIMPLE;
}
sb.append(currentChar);
break;
case '{' :
if ((state == State.SIMPLE || state == State.CURLIES) && !isEnd(previousChar)) {
state = State.CURLIES;
sb.append("(?:");
curlyLevel++;
} else {
sb.append(currentChar);
}
break;
case '}' :
if (state == State.CURLIES && curlyLevel > 0) {
sb.append(')');
currentChar = ')';
curlyLevel--;
if (curlyLevel == 0)
state = State.SIMPLE;
} else {
sb.append(currentChar);
}
break;
case ',' :
if (state == State.CURLIES) {
sb.append('|');
} else {
sb.append(currentChar);
}
break;
case '^' :
case '.' :
case '$' :
case '@' :
case '%' :
if (state == State.SIMPLE || state == State.CURLIES)
sb.append('\\');
// FALL THROUGH
default :
sb.append(currentChar);
break;
}
previousChar = currentChar;
}
return Pattern.compile(sb.toString(), flags);
}
private static boolean isStart(char c) {
return c == '(';
}
private static boolean isEnd(char c) {
return c == ')' || c == ']';
}
public void select(Collection> objects) {
for (Iterator> i = objects.iterator(); i.hasNext();) {
String s = i.next()
.toString();
if (!matches(s))
i.remove();
}
}
public void select(List> objects) {
this.select((Collection>) objects);
}
/**
* Get a list of files that match the glob expression
*
* @param root the directory to get the files from
* @param recursive to traverse the dirs recursive
* @return file list
*/
public List getFiles(File root, boolean recursive, boolean usePath) {
List result = new ArrayList<>();
getFiles(root, result, recursive, usePath);
return result;
}
public void getFiles(File root, List result, boolean recursive, boolean usePath) {
if ((root != null) && root.isDirectory()) {
File[] files = root.listFiles();
if (files != null) {
Collator collator = Collator.getInstance(Locale.ROOT);
collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
collator.setStrength(Collator.IDENTICAL); // case-sensitive
Arrays.sort(files, Comparator.comparing(File::getName, collator::compare));
for (File sub : files) {
if (sub.isFile()) {
String s = usePath ? sub.getAbsolutePath() : sub.getName();
if (matcher(s).matches()) {
result.add(sub);
}
} else if (recursive && sub.isDirectory()) {
getFiles(sub, result, recursive, usePath);
}
}
}
}
}
public static boolean in(Glob[] globs, String key) {
for (Glob g : globs) {
if (g.matcher(key)
.matches())
return true;
}
return false;
}
public static boolean in(Collection extends Glob> globs, String key) {
for (Glob g : globs) {
if (g.matcher(key)
.matches())
return true;
}
return false;
}
public int finds(CharSequence s) {
Matcher matcher = matcher(s);
if (matcher.find()) {
return matcher.start();
}
return -1;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((glob == null) ? 0 : glob.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Glob other = (Glob) obj;
return Objects.equals(glob, other.glob);
}
public boolean matches(String s) {
return matches((CharSequence) s);
}
public boolean matches(CharSequence s) {
return matcher(s).matches();
}
}