/**
* This file is part of git-as-svn. It is subject to the license terms
* in the LICENSE file found in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/gpl-2.0.html. No part of git-as-svn,
* including this file, may be copied, modified, propagated, or distributed
* except according to the terms contained in the LICENSE file.
*/
package svnserver.repository.git.path;
import org.eclipse.jgit.errors.InvalidPatternException;
import org.jetbrains.annotations.NotNull;
import svnserver.repository.git.path.matcher.name.ComplexMatcher;
import svnserver.repository.git.path.matcher.name.EqualsMatcher;
import svnserver.repository.git.path.matcher.name.RecursiveMatcher;
import svnserver.repository.git.path.matcher.name.SimpleMatcher;
import java.util.ArrayList;
import java.util.List;
/**
* Git wildcard mask.
* <p>
* Pattern format: http://git-scm.com/docs/gitignore
*
* @author Artem V. Navrotskiy <bozaro@users.noreply.github.com>
*/
public class WildcardHelper {
public static final char PATH_SEPARATOR = '/';
@NotNull
public static NameMatcher nameMatcher(@NotNull String mask) throws InvalidPatternException {
if (mask.equals("**/")) {
return RecursiveMatcher.INSTANCE;
}
final boolean dirOnly = mask.endsWith("/");
final String nameMask = tryRemoveBackslashes(dirOnly ? mask.substring(0, mask.length() - 1) : mask);
if ((nameMask.indexOf('[') < 0) && (nameMask.indexOf(']') < 0) && (nameMask.indexOf('\\') < 0)) {
// Subversion compatible mask.
if (nameMask.indexOf('?') < 0) {
int asterisk = nameMask.indexOf('*');
if (asterisk < 0) {
return new EqualsMatcher(nameMask, dirOnly);
} else if (mask.indexOf('*', asterisk + 1) < 0) {
return new SimpleMatcher(nameMask.substring(0, asterisk), nameMask.substring(asterisk + 1), dirOnly);
}
}
return new ComplexMatcher(nameMask, dirOnly, true);
} else {
return new ComplexMatcher(nameMask, dirOnly, false);
}
}
@NotNull
public static String tryRemoveBackslashes(@NotNull String pattern) {
final StringBuilder result = new StringBuilder(pattern.length());
int start = 0;
while (true) {
int next = pattern.indexOf('\\', start);
if (next == -1) {
if (start < pattern.length()) {
result.append(pattern, start, pattern.length());
}
break;
}
if (next == pattern.length() - 1) {
// Return original string.
return pattern;
}
switch (pattern.charAt(next + 1)) {
case ' ':
case '#':
case '!':
result.append(pattern, start, next);
start = next + 1;
break;
default:
return pattern;
}
}
return result.toString();
}
/**
* Split pattern with saving slashes.
*
* @param pattern Path pattern.
* @return Path pattern items.
*/
@NotNull
public static List<String> splitPattern(@NotNull String pattern) {
final List<String> result = new ArrayList<>(count(pattern, PATH_SEPARATOR) + 1);
int start = 0;
while (true) {
int next = pattern.indexOf(PATH_SEPARATOR, start);
if (next == -1) {
if (start < pattern.length()) {
result.add(pattern.substring(start));
}
break;
}
result.add(pattern.substring(start, next + 1));
start = next + 1;
}
return result;
}
/**
* Remove redundant pattern parts and make patterns more simple.
*
* @param tokens Original modifiable list.
* @return Return tokens,
*/
@NotNull
public static List<String> normalizePattern(@NotNull List<String> tokens) {
// By default without slashes using mask for files in all subdirectories
if (tokens.size() == 1 && !tokens.get(0).contains("/")) {
tokens.add(0, "**/");
}
// Normalized pattern always starts with "/"
if (tokens.size() == 0 || !tokens.get(0).equals("/")) {
tokens.add(0, "/");
}
// Replace:
// * "**/*/" to "*/**/"
// * "**/**/" to "**/"
// * "**.foo" to "**/*.foo"
int index = 1;
while (index < tokens.size()) {
final String thisToken = tokens.get(index);
final String prevToken = tokens.get(index - 1);
if (thisToken.equals("/")) {
tokens.remove(index);
continue;
}
if (thisToken.equals("**/") && prevToken.equals("**/")) {
tokens.remove(index);
continue;
}
if ((!thisToken.equals("**/")) && thisToken.startsWith("**")) {
tokens.add(index, "**/");
tokens.set(index + 1, thisToken.substring(1));
continue;
}
if (thisToken.equals("*/") && prevToken.equals("**/")) {
tokens.set(index - 1, "*/");
tokens.set(index, "**/");
index--;
continue;
}
index++;
}
// Remove tailing "**/" and "*"
while (!tokens.isEmpty()) {
final String token = tokens.get(tokens.size() - 1);
if (token.equals("**/") || token.equals("*")) {
tokens.remove(tokens.size() - 1);
} else {
break;
}
}
return tokens;
}
public static int count(@NotNull String s, char c) {
int start = 0;
int count = 0;
while (true) {
start = s.indexOf(c, start);
if (start == -1)
break;
count++;
start++;
}
return count;
}
}