com.github.protobufel.common.files.ContextPathMatchers Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common-files-v7 Show documentation
Show all versions of common-files-v7 Show documentation
Common file and directory scanning utilities
The newest version!
/*
* BSD 3-Clause License
*
* Copyright (c) 2017, David Tesler
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package com.github.protobufel.common.files;
import com.github.protobufel.common.files.PathContexts.PathContext;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.github.protobufel.common.verifications.Verifications.*;
public final class ContextPathMatchers {
private ContextPathMatchers() {}
public static HierarchicalMatcher getHierarchicalMatcher(
final String syntaxAndPattern,
final boolean isUnix,
final boolean allowDirs,
final boolean allowFiles,
final Class extends T> pathType) {
final String[] parts = verifyNonNull(syntaxAndPattern).split(":", 2);
final @NonNull String regex = verifyArgument(parts.length == 2, parts[1]);
if (parts[0].equals("regex")) {
return new SimpleHierarchicalMatcher(isUnix, regex, allowDirs, allowFiles);
} else if (parts[0].equals("glob")) {
return new SimpleHierarchicalMatcher(regex, isUnix, allowDirs, allowFiles);
} else {
throw new UnsupportedOperationException(String.format("%s syntax", parts[0]));
}
}
interface HierarchicalMatcherCommon {
boolean isEmpty();
boolean isAllowDirs();
boolean isAllowFiles();
String getPattern();
}
public interface ContextHierarchicalMatcher extends HierarchicalMatcherCommon {
boolean matches(T path, PathContext pathContext);
DirectoryMatchResult matchesDirectory(T path, PathContext pathContext);
}
public interface BasicHierarchicalMatcher extends HierarchicalMatcherCommon {
boolean matchesResolved(@Nullable String path, PathContext pathContext);
DirectoryMatchResult matchesResolvedDirectory(
@Nullable String path, @Nullable String separator, PathContext pathContext);
}
public interface HierarchicalMatcher
extends ContextHierarchicalMatcher, BasicHierarchicalMatcher {}
public static final class SimpleHierarchicalMatcher implements HierarchicalMatcher {
@SuppressWarnings("null")
private static final Pattern UNIX_TO_WINDOWS_SLASH_FIND_REGEX =
Pattern.compile("/|(?:(\\\\*+)(?:(\\[)|(\\])|(Q)|(E)))");
private final Pattern pattern;
private final boolean allowDirs;
private final boolean allowFiles;
private final boolean isUnix;
public SimpleHierarchicalMatcher(final SimpleHierarchicalMatcher other) {
this.pattern = other.pattern;
this.allowDirs = other.allowDirs;
this.allowFiles = other.allowFiles;
this.isUnix = other.isUnix;
}
public SimpleHierarchicalMatcher(
final String glob,
final boolean isUnix,
final boolean allowDirs,
final boolean allowFiles) {
this(isUnix, convertGlobToRegex(glob, isUnix), true, allowDirs, allowFiles);
}
public SimpleHierarchicalMatcher(
final boolean isUnix,
final String regex,
final boolean allowDirs,
final boolean allowFiles) {
this(isUnix, regex, false, allowDirs, allowFiles);
}
public SimpleHierarchicalMatcher(
final boolean isUnix,
final String regex,
final boolean isRegexSystemSpecific,
final boolean allowDirs,
final boolean allowFiles) {
verifyCondition((allowDirs || allowFiles), "allowDirs and allowFiles cannot be both false");
this.allowDirs = allowDirs;
this.allowFiles = allowFiles;
final String regexFlags = isUnix ? "" : "(?iu)";
final String fsRegex =
isRegexSystemSpecific ? regex : convertRegexToSystemSpecific(regex, isUnix);
@SuppressWarnings("null")
@NonNull
Pattern compiled = Pattern.compile(regexFlags + verifyNonNull(fsRegex));
this.pattern = compiled;
this.isUnix = isUnix;
}
protected static final String convertGlobToRegex(final String glob, final boolean isUnix) {
if (isUnix) {
return Globs.toUnixRegexPattern(glob);
} else {
return Globs.toWindowsRegexPattern(glob);
}
}
@SuppressWarnings("null")
@Override
public String getPattern() {
return pattern.pattern();
}
@Override
public String toString() {
return "SimpleHierarchicalMatcher [pattern="
+ pattern
+ ", flags="
+ pattern.flags()
+ ", allowDirs="
+ allowDirs
+ ", allowFiles="
+ allowFiles
+ ", isUnix="
+ isUnix
+ "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (allowDirs ? 1231 : 1237);
result = prime * result + (allowFiles ? 1231 : 1237);
result = prime * result + (isUnix ? 1231 : 1237);
result = prime * result + pattern.pattern().hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof SimpleHierarchicalMatcher)) {
return false;
}
final SimpleHierarchicalMatcher> other = (SimpleHierarchicalMatcher>) obj;
if (allowDirs != other.allowDirs) {
return false;
}
if (allowFiles != other.allowFiles) {
return false;
}
if (isUnix != other.isUnix) {
return false;
}
if (!pattern.pattern().equals(other.pattern.pattern())) {
return false;
}
return true;
}
@Override
public boolean isEmpty() {
return "".equals(pattern.pattern());
}
public boolean isAllowDirs() {
return allowDirs;
}
public boolean isAllowFiles() {
return allowFiles;
}
@Override
public boolean matches(final T path, final PathContext pathContext) {
return matchesResolved(pathContext.resolvePath(path), pathContext);
}
@Override
public DirectoryMatchResult matchesDirectory(final T path, final PathContext pathContext) {
return matchesResolvedDirectory(
pathContext.resolvePath(path), pathContext.getSeparator(path), pathContext);
}
@Override
public boolean matchesResolved(final @Nullable String path, final PathContext pathContext) {
return (allowFiles && (path != null)) ? pattern.matcher(path).matches() : false;
}
@Override
public DirectoryMatchResult matchesResolvedDirectory(
final @Nullable String path,
final @Nullable String separator,
final PathContext pathContext) {
if ((path == null) || (separator == null)) {
return DirectoryMatchResult.NO_MATCH;
}
final String pathWithSlash = path + separator;
final Matcher matcher;
final boolean isMatched;
if (allowDirs) {
matcher = pattern.matcher(path);
isMatched = matcher.matches();
matcher.reset(pathWithSlash);
} else {
matcher = pattern.matcher(pathWithSlash);
isMatched = false;
}
final boolean skip = matcher.matches() ? matcher.requireEnd() : (!matcher.hitEnd());
return DirectoryMatchResult.valueOf(isMatched, skip);
}
protected final String convertRegexToSystemSpecific(
final String normalizedRelativeRegex, final boolean isUnix) {
if (isUnix) {
return normalizedRelativeRegex;
} else {
return convertNormalizedRelativeRegexToWindows(normalizedRelativeRegex);
}
}
protected final String convertNormalizedRelativeRegexToWindows(
final String normalizedRelativeRegex) {
@SuppressWarnings("null")
final @NonNull Matcher matcher =
UNIX_TO_WINDOWS_SLASH_FIND_REGEX.matcher(normalizedRelativeRegex);
final StringBuffer sb = new StringBuffer();
boolean foundCharGroup = false;
boolean foundQuote = false;
while (matcher.find()) {
if (foundQuote) {
if (matcher.group(5) != null) {
if ((matcher.group(1).length() % 2) == 1) {
foundQuote = false;
}
}
} else if (matcher.group(4) != null) {
if ((matcher.group(1).length() % 2) == 1) {
foundQuote = true;
}
} else if (matcher.group(2) != null) {
if ((matcher.group(1).length() % 2) == 0) {
foundCharGroup = true;
}
} else if (matcher.group(3) != null) {
if ((matcher.group(1).length() % 2) == 0) {
foundCharGroup = false;
}
} else if (!foundCharGroup) {
matcher.appendReplacement(sb, "\\\\\\\\");
}
}
@SuppressWarnings("null")
final @NonNull String result = matcher.appendTail(sb).toString();
return result;
}
}
}