package org.dcache.util;
import com.google.common.base.CharMatcher;
import java.io.Serializable;
import java.util.regex.Pattern;
/**
* A glob is a pattern used for limited pattern matching. The class
* supports the following glob patterns:
*
* <ul>
* <li><tt>?</tt> Matches any single character.</li>
* <li><tt>*</tt> Matches any sequence of zero or more characters.</li>
* <li><tt>{a,b,...}</tt> Matches any of the sub-patterns a, b, etc.</li>
* </ul>
*
* The current implementation does not have an escape symbol.
*/
public class Glob implements Serializable
{
private static final long serialVersionUID = -5052804169005574207L;
private static final CharMatcher WILDCARD = CharMatcher.anyOf("*?{");
private final String _pattern;
public Glob(String s)
{
_pattern = s;
}
public boolean matches(String s)
{
return toPattern().matcher(s).matches();
}
public boolean isGlob()
{
return isGlob(_pattern);
}
@Override
public String toString()
{
return _pattern;
}
public Pattern toPattern()
{
return parseGlobToPattern(_pattern);
}
public static Pattern parseGlobToPattern(String glob)
{
StringBuilder s = new StringBuilder(glob.length() * 2 + 2);
int j = 0;
s.append('^');
for (int i = 0; i < glob.length(); i++) {
switch (glob.charAt(i)) {
case '?':
s.append(Pattern.quote(glob.substring(j, i)));
s.append('.');
j = i + 1;
break;
case '*':
s.append(Pattern.quote(glob.substring(j, i)));
s.append(".*");
j = i + 1;
break;
case '{':
s.append(Pattern.quote(glob.substring(j, i)));
i = parseCurlyBrackets(glob, i, s);
j = i + 1;
break;
default:
break;
}
}
s.append(Pattern.quote(glob.substring(j)));
s.append('$');
return Pattern.compile(s.toString());
}
private static int parseCurlyBrackets(String glob, int from, StringBuilder out)
{
assert glob.charAt(from) == '{';
StringBuilder s = new StringBuilder();
int j = from + 1;
for (int i = j; i < glob.length(); i++) {
switch (glob.charAt(i)) {
case '?':
s.append(Pattern.quote(glob.substring(j, i)));
s.append('.');
j = i + 1;
break;
case '*':
s.append(Pattern.quote(glob.substring(j, i)));
s.append(".*");
j = i + 1;
break;
case ',':
s.append(Pattern.quote(glob.substring(j, i)));
s.append('|');
j = i + 1;
break;
case '{':
s.append(Pattern.quote(glob.substring(j, i)));
i = parseCurlyBrackets(glob, i, s);
j = i + 1;
break;
case '}':
out.append('(').append(s).append(Pattern.quote(glob.substring(j, i))).append(')');
return i;
default:
break;
}
}
// Unterminated curly brace
out.append(Pattern.quote("{"));
return from;
}
public static boolean isGlob(String s)
{
return WILDCARD.matchesAnyOf(s);
}
/**
* Unfolds alternations (brace lists) into a list.
*/
public static Iterable<String> expandGlob(String glob)
{
return new GlobBraceParser(glob).expandGlob();
}
/**
* Similar to {@code expandGlob} but considers the input
* string to be a comma separated list. Equivalient to calling
* {@code expandGlob("{" + glob + "}")}.
*/
public static Iterable<String> expandList(String glob)
{
return new GlobBraceParser(glob).expandList();
}
}