package com.beijunyi.parallelgit.filesystem.utils; import java.util.regex.PatternSyntaxException; import javax.annotation.Nonnull; public final class GitGlobs { private static final String REGEX_META_CHARS = ".^$+{[]|()"; private static final String GLOB_META_CHARS = "\\*?[{"; private static final char EOL = 0; private static boolean isRegexMeta(char c) { return REGEX_META_CHARS.indexOf(c) != -1; } private static boolean isGlobMeta(char c) { return GLOB_META_CHARS.indexOf(c) != -1; } private static char charAt(String glob, int i) { return i < glob.length() ? glob.charAt(i) : EOL; } @Nonnull public static String toRegexPattern(String globPattern) { boolean inGroup = false; StringBuilder regex = new StringBuilder("^"); int i = 0; while(i < globPattern.length()) { char c = globPattern.charAt(i++); switch(c) { case '\\': // escape special characters if(i == globPattern.length()) throw new PatternSyntaxException("No character to escape", globPattern, i - 1); char next = globPattern.charAt(i++); if(isGlobMeta(next) || isRegexMeta(next)) regex.append('\\'); regex.append(next); break; case '/': regex.append(c); break; case '[': regex.append("[[^/]&&["); if(charAt(globPattern, i) == '^') { // escape the regex negation char if it appears regex.append("\\^"); i++; } else { // negation if(charAt(globPattern, i) == '!') { regex.append('^'); i++; } // hyphen allowed at start if(charAt(globPattern, i) == '-') { regex.append('-'); i++; } } boolean hasRangeStart = false; char last = 0; while(i < globPattern.length()) { c = globPattern.charAt(i++); if(c == ']') break; if(c == '/') throw new PatternSyntaxException("Explicit 'name separator' in class", globPattern, i - 1); // TBD: how to specify ']' in a class? if(c == '\\' || c == '[' || c == '&' && charAt(globPattern, i) == '&') { // escape '\', '[' or "&&" for regex class regex.append('\\'); } regex.append(c); if(c == '-') { if(!hasRangeStart) throw new PatternSyntaxException("Invalid range", globPattern, i - 1); if((c = charAt(globPattern, i++)) == EOL || c == ']') break; if(c < last) throw new PatternSyntaxException("Invalid range", globPattern, i - 3); regex.append(c); hasRangeStart = false; } else { hasRangeStart = true; last = c; } } if(c != ']') throw new PatternSyntaxException("Missing ']", globPattern, i - 1); regex.append("]]"); break; case '{': if(inGroup) throw new PatternSyntaxException("Cannot nest groups", globPattern, i - 1); regex.append("(?:(?:"); inGroup = true; break; case '}': if(inGroup) { regex.append("))"); inGroup = false; } else { regex.append('}'); } break; case ',': if(inGroup) regex.append(")|(?:"); else regex.append(','); break; case '*': if(charAt(globPattern, i) == '*') { // crosses directory boundaries regex.append(".*"); i++; } else { // within directory boundary regex.append("[^/]*"); } break; case '?': regex.append("[^/]"); break; default: if(isRegexMeta(c)) regex.append('\\'); regex.append(c); } } if(inGroup) throw new PatternSyntaxException("Missing '}", globPattern, i - 1); return regex.append('$').toString(); } }