/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.utils.common;
import java.io.IOException;
import java.util.regex.Pattern;
import org.apache.commons.io.FilenameUtils;
/**
* Utility class for handling filenames.
*
* @author Jan Flink
* @author Tobias Rodehutskors
*/
public final class CrossPlatformFilenameUtils {
/**
* There is a bunch of filenames which are forbidden on Windows, even though they are composed of valid characters.
*/
public static final String[] FORBIDDEN_WINDOWS_FILENAMES = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5",
"COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
/**
* Forbidden characters for filenames.
*/
public static final char[] FORBIDDEN_CHARACTERS = {
'/', // forbidden on Linux
'\\', // forbidden on ...
':',
'\"',
'*',
'?',
'<',
'>',
'|' }; // ... Windows
/**
* Forbidden characters on windows platforms.
*/
private static final String[] WINDOWS_FORBIDDEN_FILENAME_PATTERNS = {
// control characters are not allowed
"\\x00-\\x1F",
"\\x7F",
// whitespace is not allowed (space is allowed)
"\\t",
"\\n",
"\\f",
"\\r",
};
/**
* Other forbidden regex.
*/
private static final String[] FORBIDDEN_REGEX = {
/**
* .* matches any character (except for line terminators) between zero and unlimited times \.* matches the character . literally
* (case sensitive) + Matches between one and unlimited times $ asserts position at the end of the string
*/
".*\\.+$"
};
private static final int MAXIMUM_FILENAME_LENGTH = 240;
private static final String FORWARD_SLASH = "/";
private static Pattern forbiddenCharacterPattern;
static {
String regExp = "[".concat(new String(FORBIDDEN_CHARACTERS));
regExp = regExp.concat(org.apache.commons.lang3.StringUtils.join(WINDOWS_FORBIDDEN_FILENAME_PATTERNS));
regExp = regExp.concat("]");
// compile this pattern once and store it for reuse
forbiddenCharacterPattern = Pattern.compile(regExp);
}
private static Pattern forbiddenFilenamePattern;
static {
// some filenames are forbidden, and also not be used with an optional extension:
// (\\..*) is a dot followed by * arbitrary characters, e.g. ".test"
String regExp = FORBIDDEN_WINDOWS_FILENAMES[0] + "(\\..*)?";
for (int i = 1; i < FORBIDDEN_WINDOWS_FILENAMES.length; i++) {
regExp = regExp.concat("|" + FORBIDDEN_WINDOWS_FILENAMES[i] + "(\\..*)?");
}
for (String current : FORBIDDEN_REGEX) {
regExp = regExp.concat("|" + current);
}
// compile this pattern once and store it for reuse
forbiddenFilenamePattern = Pattern.compile(regExp);
}
private static Pattern nfsFilePattern;
static {
// .nfsXXXX files are an artifact of the NFS "silly rename" (http://nfs.sourceforge.net/#faq_d2) and should be ignored
String regExp = "^\\.nfs.+$";
// compile this pattern once and store it for reuse
nfsFilePattern = Pattern.compile(regExp);
}
private CrossPlatformFilenameUtils() {};
/**
* Checks the validity of a given filename on all platforms.
*
* @param filename The filename to check
* @return True, if the filename is valid on all platforms.
*/
public static boolean isFilenameValid(String filename) {
if (filename.length() == 0 || filename.length() > MAXIMUM_FILENAME_LENGTH) {
return false;
}
// the filename should neither contain a forbidden character nor should it be forbidden by itself
return !forbiddenCharacterPattern.matcher(filename).find() && !forbiddenFilenamePattern.matcher(filename).matches();
}
/**
* Checks the validity of a given filename on all platforms and throws an {@link InvalidFilenameException} if it is not valid.
*
* @param filename The filename to check
* @throws InvalidFilenameException if the filename is not valid on all platforms
*/
public static void throwExceptionIfFilenameNotValid(String filename) throws InvalidFilenameException {
if (!isFilenameValid(filename)) {
throw new InvalidFilenameException(filename);
}
}
/**
* Checks the validity of a given filename on all platforms and throws an {@link IOException} if it is not valid.
*
* @param filename The filename to check
* @throws IOException if the filename is not valid on all platforms
*/
public static void throwIOExceptionIfFilenameNotValid(String filename) throws IOException {
if (!isFilenameValid(filename)) {
throw new IOException(StringUtils.format(InvalidFilenameException.INVALID_FILENAME_MESSAGE_TEMPLATE, filename));
}
}
/**
* Checks the validity of a given path on all platforms.
*
* @param path The path to check
* @return True, if path is valid on all platforms
*/
public static boolean isPathValid(String path) {
path = FilenameUtils.separatorsToUnix(path);
// Remove the drive letters
path = path.replaceAll("(^[a-zA-Z]{1}[:]{1})", "");
for (String pathSegment : path.split(FORWARD_SLASH)) {
if (pathSegment.isEmpty()) {
continue;
}
if (!isFilenameValid(pathSegment)) {
return false;
}
}
return true;
}
/**
* Checks if the filename is a .nfsXXXX file.
*
* @param filename The file name to check.
* @return True, if it is a .nfsXXX file.
*/
public static boolean isNFSFile(String filename) {
return nfsFilePattern.matcher(filename).matches();
}
}