package jdrivesync.cli;
import jdrivesync.exception.JDriveSyncException;
import jdrivesync.logging.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FileNamePatterns {
private static final Logger LOGGER = LoggerFactory.getLogger();
private List<FileNamePattern> fileNamePatterns;
private enum PatternType {
Path, Filename, Foldername
}
private static class FileNamePattern {
private final PatternType patternType;
private final Pattern pattern;
private FileNamePattern(PatternType patternType, Pattern pattern) {
this.patternType = patternType;
this.pattern = pattern;
}
public static FileNamePattern create(String pattern) {
if (pattern.contains("**")) {
if (count(pattern, "**") > 1) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern, because it contains the double asterisks more than once: '" + pattern + "'.");
}
if (pattern.startsWith("**/")) {
if(pattern.length() == 3) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern, because it contains only the double asterisks: '" + pattern + "'.");
}
Pattern regPattern;
try {
regPattern = Pattern.compile(escapeForRegEx(pattern.substring(3)));
} catch (Exception e) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern '" + pattern + "':" + e.getMessage(), e);
}
return new FileNamePattern(PatternType.Filename, regPattern);
} else if (pattern.endsWith("/**")) {
if(pattern.length() == 3) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern, because it contains only the double asterisks: '" + pattern + "'.");
}
Pattern regPattern;
try {
regPattern = Pattern.compile(escapeForRegEx(pattern.substring(0, pattern.length() - 2)) + ".*");
} catch (Exception e) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern '" + pattern + "':" + e.getMessage(), e);
}
return new FileNamePattern(PatternType.Path, regPattern);
} else if (pattern.contains("/**/")) {
if(pattern.length() == 4) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern, because it contains only the double asterisks: '" + pattern + "'.");
}
int indexOf = pattern.indexOf("/**/");
Pattern regPattern;
try {
regPattern = Pattern.compile(escapeForRegEx(pattern.substring(0, indexOf)) + ".*" + escapeForRegEx(pattern.substring(indexOf + 3, pattern.length())));
} catch (Exception e) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern '" + pattern + "':" + e.getMessage(), e);
}
return new FileNamePattern(PatternType.Path, regPattern);
} else {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern '" + pattern + "', because it does not start with **/ or end with **/ or contain /**/.");
}
} else if (pattern.contains("/")) {
if (pattern.endsWith("/")) {
Pattern regPattern;
try {
if(pattern.length() > 1) {
pattern = pattern.substring(0, pattern.length()-1);
}
regPattern = Pattern.compile(escapeForRegEx(pattern));
} catch (Exception e) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern '" + pattern + "':" + e.getMessage(), e);
}
return new FileNamePattern(PatternType.Foldername, regPattern);
} else {
Pattern regPattern;
try {
regPattern = Pattern.compile(escapeForRegEx(pattern));
} catch (Exception e) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern '" + pattern + "':" + e.getMessage(), e);
}
return new FileNamePattern(PatternType.Path, regPattern);
}
} else {
Pattern regPattern;
try {
regPattern = Pattern.compile(escapeForRegEx(pattern));
} catch (Exception e) {
throw new JDriveSyncException(JDriveSyncException.Reason.InvalidCliParameter, "Invalid ignore pattern '" + pattern + "':" + e.getMessage(), e);
}
return new FileNamePattern(PatternType.Filename, regPattern);
}
}
private static int count(String pattern, String part) {
int count = 0;
int indexOf = pattern.indexOf(part);
while (indexOf != -1) {
count++;
int newStartIndex = indexOf + part.length();
if (newStartIndex < pattern.length()) {
indexOf = pattern.indexOf(part, newStartIndex);
} else {
indexOf = -1;
}
}
return count;
}
private static String escapeForRegEx(String pattern) {
pattern = pattern.toLowerCase();
pattern = pattern.replace(".", "\\.");
pattern = pattern.replace("&", "\\&");
pattern = pattern.replace("+", "\\+");
pattern = pattern.replace("?", "\\?");
pattern = pattern.replace("{", "\\{");
pattern = pattern.replace("}", "\\}");
pattern = pattern.replace("[", "\\[");
pattern = pattern.replace("]", "\\]");
pattern = pattern.replace("*", "[^/]*");
return pattern;
}
public boolean matches(String path, boolean isDirectory) {
if (patternType == PatternType.Path) {
Matcher matcher = pattern.matcher(path);
return matcher.matches();
} else if (patternType == PatternType.Filename) {
Matcher matcher = pattern.matcher(getFilename(path));
return matcher.matches();
} else if (patternType == PatternType.Foldername) {
if(isDirectory) {
Matcher matcher = pattern.matcher(path);
return matcher.matches();
}
}
return false;
}
private String getFilename(String path) {
int lastIndexOf = path.lastIndexOf("/");
if (lastIndexOf != -1 && lastIndexOf + 1 < path.length()) {
return path.substring(lastIndexOf + 1, path.length());
}
return path;
}
}
private FileNamePatterns(List<FileNamePattern> fileNamePatterns) {
this.fileNamePatterns = fileNamePatterns;
}
public static FileNamePatterns create(List<String> lines) {
List<FileNamePattern> fileNamePatternList = new ArrayList<>(lines.size());
int lineCount = 0;
for (String line : lines) {
lineCount++;
line = line.trim();
if (line.length() == 0) {
continue;
}
if (line.startsWith("#")) {
LOGGER.log(Level.FINE, "Skipping commented line " + lineCount + ".");
continue;
}
line = line.replace("\\#", "#");
FileNamePattern fileNamePattern = FileNamePattern.create(line);
fileNamePatternList.add(fileNamePattern);
}
return new FileNamePatterns(fileNamePatternList);
}
public boolean matches(String path, boolean isDirectory) {
path = path.toLowerCase();
if (path.startsWith("/")) {
if (path.length() > 1) {
path = path.substring(1);
} else {
return true;
}
}
for (FileNamePattern fileNamePattern : fileNamePatterns) {
if(fileNamePattern.matches(path, isDirectory)) {
return true;
}
}
return false;
}
}