All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.sfac.file.FilePathUtils Maven / Gradle / Ivy

Go to download

This project is the model side of the Swing Framework and Components (SFaC). If your doing a clean separation between model (or business) and view (or GUI or rendering) parts of your application, (like in the MVC pattern), then the only classes of SFaC your model can access are in this project. On the other hand, the classes in sfac-core project are GUI-specific and should not be known by your model.

The newest version!
/*-------------------------------------------------------------------------
 Copyright 2009 Olivier Berlanger

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 -------------------------------------------------------------------------*/
package net.sf.sfac.file;


import java.io.File;
import java.util.ArrayList;
import java.util.List;

import net.sf.sfac.utils.Comparison;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * Utilities to manipulate file paths as strings. 
* In particular it can convert absolute to relative paths (and vice-versa). This utility class is quite platform-independent * beacuse it handles indifferently slash or backslash as path separator. The only platform-specific thing is that it ignores case * in path comparison (wich is mandatory on Windows system), this is detected fom the file system or can be passed as a constructor * parameter. * * @author Olivier Berlanger */ public class FilePathUtils { private static Log log = LogFactory.getLog(FilePathUtils.class); private static char separatorChar = File.separatorChar; /** Flag saying if file comparison should not be caseSensitive. */ private boolean ignoreCase; /** The base drive (null on non-windows system) used a default drive for relative paths. */ private String appBaseDrive; /** * The base path used a starting point for relative paths.
* Note that this path also includes the drive (if applicable). */ private String appBasePath; /** * Create a FilePathUtils bounded to the given local base directory. The FilePathUtils will use the separator char of the * current system. * * @param baseDir * The base directory used a starting point for relative paths. * @exception IllegalArgumentException * if the given base directory is not a directory of cannot be canonicalised. */ public FilePathUtils(File baseDir) { ignoreCase = checkFileSystemIgnoreCase(); try { appBasePath = baseDir.getCanonicalPath(); appBaseDrive = getDrive(appBasePath); } catch (Exception e) { throw new IllegalArgumentException("The base directory '" + baseDir + "' is invalid !", e); } } /** * Create a FilePathUtils bounded to the given base directory. You can use this constructor to pass other parameters than the * defaults of the current system, for example, if you want to calculate path for a remote system. * * @param basePath * The canonicalized base path (with the drive if present). This class will use (when creating paths) the path * separator given in this base reference path. * @param ignoreCase * true if the target file system is not case sensitive. * @exception IllegalArgumentException * if the given base directory cannot be canonicalised. */ public FilePathUtils(String basePath, boolean ignoreCaseInPaths) { ignoreCase = ignoreCaseInPaths; separatorChar = (basePath.indexOf('\\') >= 0) ? '\\' : '/'; try { appBaseDrive = getDrive(basePath); appBasePath = canonicalize(basePath); } catch (Exception e) { throw new IllegalArgumentException("The base directory '" + basePath + "' is invalid !", e); } } /** * Get the preferred file separator char ('/' or '\') of this FilePathUtils. Though there is one preferred separator char, both * '\' and '/' are always accepted in input paths. * * @return the preferred file separator char ('/' or '\') */ public char getSparatorChar() { return separatorChar; } /** * Check if the local file system is case sensitive. */ private boolean checkFileSystemIgnoreCase() { boolean fileSystemIgnoresCase = true; boolean checked = false; try { String tempDir = System.getProperty("java.io.tmpdir"); File fil = new File(tempDir); String filePath = fil.getAbsolutePath(); String uppercaseFilePath = filePath.toUpperCase(); String lowercaseFilePath = filePath.toLowerCase(); if (!lowercaseFilePath.equals(uppercaseFilePath)) { File uppercaseFile = new File(uppercaseFilePath); File lowercaseFile = new File(lowercaseFilePath); fileSystemIgnoresCase = uppercaseFile.exists() && lowercaseFile.exists(); checked = true; } if (!checked) { log.warn("Cannot check if file system is case-senstive (assume it is not)"); } } catch (Exception e) { log.error("Cannot check if file system is case-senstive (assume it is not)", e); } return fileSystemIgnoresCase; } /** * Get the base path used a starting point for relative paths.
* Note: this path also includes the drive (if applicable). * * @return the base path used a starting point for relative paths. */ public String getBasePath() { return appBasePath; } /** * Get an absolute file path for the given path. If the given path is a relative one, it will be added to the base path. The * returned path will be canonicalized. Returns null if the given path is null. * * @param filePath * the file path to make absolute. * @return an absolute file path corresponding to the given one. * @exception InvalidPathException * if the given path can not be combined with the base path to produce a canonicalised path. */ public String getAbsoluteFilePath(String filePath) throws InvalidPathException { if (filePath == null) return null; if (isWhite(filePath) || isThisPath(filePath)) return appBasePath; if (!isAbsolute(filePath)) filePath = concatPaths(appBasePath, filePath); // be sure that the absolute path starts with a drive name (on Win32) if ((appBaseDrive != null) && (getDrive(filePath) == null)) { filePath = appBaseDrive + filePath; } String result = canonicalize(filePath); if (result.startsWith("..")) throw new InvalidPathException("Too many '..' in path '" + filePath + "'"); return result; } /** * Canonicalize the given path.
* The path can be absolute or relative, all the ".." that cannot be resolved will remain at the begin of the resulting path. * * @param originalPath * a path to canonicalize. * @return the given path canonicalized. * @exception InvalidPathException * If the path cannot be canonicalized for any reason. */ public String canonicalize(String originalPath) throws InvalidPathException { if (originalPath == null) return null; if (Comparison.isEmpty(originalPath)) return "."; boolean startWithSlash = startsWithSlash(originalPath); // add drive letter if applicable if (startWithSlash && (appBaseDrive != null)) { originalPath = appBaseDrive + originalPath; startWithSlash = false; } // tokenize path List tokens = tokenize(originalPath); // canonicalize path canonicalize(tokens); // rebuild the absolutePath. if (startWithSlash && (tokens.size() > 0) && (tokens.get(0).equals(".."))) { throw new InvalidPathException("Too many '..' in path '" + originalPath + "'"); } return buildPath(tokens, startWithSlash); } private void canonicalize(List tokens) throws InvalidPathException { // remove any "." or ".." int len = tokens.size(); for (int i = 0; i < len; i++) { String token = tokens.get(i); if (token.equals(".")) { tokens.remove(i); i--; len--; } else if (token.equals("..")) { if (i > 0) { String previousToken = tokens.get(i - 1); if (previousToken.indexOf(':') >= 0) { throw new InvalidPathException("Too many '..' in path conflicting with drive letter '" + previousToken + "\\'"); } else if (!previousToken.equals("..")) { tokens.remove(i); tokens.remove(i - 1); i -= 2; len -= 2; } } } else { checkPathItem(token, i); } } } private String buildPath(List tokens, boolean startWithSlash) { int len = tokens.size(); if (len == 0) return startWithSlash ? String.valueOf(separatorChar) : "."; StringBuilder sb = new StringBuilder(); if (startWithSlash) sb.append(separatorChar); for (int i = 0; i < len; i++) { if (i > 0) sb.append(separatorChar); sb.append(tokens.get(i)); } if ((len == 1) && tokens.get(0).endsWith(":")) { sb.append(separatorChar); } return sb.toString(); } private List tokenize(String path) { List tokens = new ArrayList(); StringBuilder sb = new StringBuilder(); int len = path.length(); char ch; for (int i = 0; i < len; i++) { ch = path.charAt(i); if ((ch == '/') || (ch == '\\')) { if (sb.length() > 0) { tokens.add(sb.toString()); sb.setLength(0); } } else { sb.append(ch); } } if (sb.length() > 0) { tokens.add(sb.toString()); } return tokens; } /** * Check if a path item is valid.
* Checked rules: *
    *
  • The path item length cannot be 0 *
  • The path item cannot contain following chars: ':', '?', '*', '|', '"', '<', '>', '/', '\' *
* * @param path * The path containing the item to check (only used for exception reporting) * @param str * The path item to check. * @exception InvalidPathException * If the path item is invalid. */ private void checkPathItem(String str, int pos) throws InvalidPathException { int len = str.length(); if (len == 0) throw new InvalidPathException("Empty path item"); for (int i = 0; i < len; i++) { switch (str.charAt(i)) { case ':': if ((len == 2) && (i == 1) && (pos == 0)) { // allowed only just after the drive letter break; } case '?': case '*': case '|': case '"': case '<': case '>': case '/': case '\\': case '\n': case '\t': throw new InvalidPathException("Illegal character '" + str.charAt(i) + "' in path item '" + str + "'"); default: // valid char } } } /** * Get the given path expressed as relative to this class base directory (if possible).
* If the given path is already relative, it's returned without changes. Returns null if the given path is null.
* Note: the result can be an absolute path, if the given path is absolute and has no common part with the base directory. * * @param filePath * The path to express as relative to this class base directory. * @return The given path expressed as relative to this class base directory. */ public String getRelativeFilePath(String filePath) throws InvalidPathException { if (filePath == null) return null; if (isWhite(filePath) || isThisPath(filePath)) return "."; if (!isAbsolute(filePath)) return canonicalize(filePath); if ((appBaseDrive != null) && startsWithSeparator(filePath)) filePath = appBaseDrive + filePath; List baseTokens = tokenize(appBasePath); List pathTokens = tokenize(filePath); int len = Math.min(baseTokens.size(), pathTokens.size()); // search the first difference index int diffIndex; for (diffIndex = 0; diffIndex < len; diffIndex++) { if (!equalsCheckCase(baseTokens.get(diffIndex), pathTokens.get(diffIndex))) { break; } } // build relative path in List List relativeTokens; boolean startWithSlash = false; if (diffIndex == 0) { relativeTokens = pathTokens; startWithSlash = appBaseDrive == null; } else { relativeTokens = new ArrayList(); for (int i = 0; i < baseTokens.size() - diffIndex; i++) { relativeTokens.add(".."); } for (int i = diffIndex; i < pathTokens.size(); i++) { relativeTokens.add(pathTokens.get(i)); } } canonicalize(relativeTokens); // rebuild the absolutePath. if (startWithSlash && (relativeTokens.size() > 0) && (relativeTokens.get(0).equals(".."))) { throw new InvalidPathException("Too many '..' in path '" + filePath + "'"); } return buildPath(relativeTokens, startWithSlash); } private static boolean startsWithSeparator(String str) { return str.startsWith("/") || str.startsWith("\\"); } private static boolean endsWithSeparator(String str) { return str.endsWith("/") || str.endsWith("\\"); } private static boolean isThisPath(String str) { return "./".equals(str) || ".\\".equals(str) || ".".equals(str); } /** * Check if the given file path is absolute.
* A file path is absolute when it starts with a drive letter on windows system, and if it starts with a separator (usually * slash) on other systems. * * @param filePath * the file path to check * @return true iff the given file path is absolute. */ public static boolean isAbsolute(String filePath) { if (filePath == null) return false; if ((separatorChar == '\\') && (filePath.indexOf(':') > 0)) return true; return startsWithSeparator(filePath); } /** * Check if the given file path start with a separator (usually slash or backslash). * * @param filePath * the file path to check * @return true iff the given file path starts with the file separator. */ public static boolean startsWithSlash(String filePath) { if (filePath == null) return false; int len = filePath.length(); if (len < 1) return false; return separatorChar == filePath.charAt(0); } /** * Get the drive of a path or null if no drive is specified in the path.
* The returned drive string contains the drive letter and the colon (like "C:"). * * @param filePath * The file path possibly starting with a drive letter. * @return The drive of a path or null if no drive is specified in the path. */ public static String getDrive(String filePath) { if (filePath == null) return null; int colonIndex = filePath.indexOf(':'); return (colonIndex > 0) ? filePath.substring(0, colonIndex + 1) : null; } /** * Concatenate two paths to one path.
* The first path can be any path (relative or absolute) or can even be null. If the second path is absolute, this path is * returned as is. If it is a relative path, this method concatenate the two taking care if there is a file separator to add * between the two concatenated paths (and if the first path is not empty). * * @param firstPath * first path to concatenate. * @param secondPath * second path to concatenate. * @return the two paths concatenated to one path. */ public static String concatPaths(String firstPath, String secondPath) { if (isWhite(secondPath)) return firstPath; if (isAbsolute(secondPath)) return secondPath; if (isWhite(firstPath) || isThisPath(firstPath)) return secondPath; if (endsWithSeparator(firstPath)) return firstPath + secondPath; return firstPath + File.separator + secondPath; } /** * Check if the given strings are the same. The check will or will not be case sensitive depending of the * IGNORE_CASE flag. * * @param str1 * String to check. * @param str2 * String to check. */ private boolean equalsCheckCase(String str1, String str2) { if (str1 == null) return str2 == null; if (ignoreCase) return str1.equalsIgnoreCase(str2); return str1.equals(str2); } /** * Check if a string is null or contains only whitespaces. * * @param str * string to check * @return true iff the given string is null or contains only whitespaces. */ private static boolean isWhite(String str) { if (str == null) return true; int len = str.length(); for (int i = 0; i < len; i++) { if (str.charAt(i) > ' ') return false; } return true; } /** * Get the extension of the given file (ex: extension of "myIcon.gif" returns "gif").
* This method simply returns the text after the last dot, or null if there is no dot. * * @param fileName * a file name * @return the extension of the given file. */ public static String getExtension(String fileName) { if (fileName == null) return null; int lastDotIndex = fileName.lastIndexOf('.'); if (lastDotIndex < 0) return null; if (fileName.lastIndexOf('/') > lastDotIndex) return null; if (fileName.lastIndexOf('\\') > lastDotIndex) return null; return fileName.substring(lastDotIndex + 1); } /** * Get the directory part of a full file path.
* Example: directory for "\Temp\test\MyFile.txt" is "\Temp\test".
* If there is no directory, null is returned.
* Example: directory for "MyFile.txt" is null. *

* Note: This method does not try to interpret the path, the last item of the path is assumed to be a file name (even if it is a * directory). So directory for "\Temp\test" is "\Temp", and directory for "\Temp\" is "\Temp". *

* * @param filePath * a full file path */ public static String getDirectoryPath(String filePath) { if (filePath == null) return null; int lastSlashIndex = filePath.lastIndexOf('/'); int lastBackSlashIndex = filePath.lastIndexOf('\\'); int index = Math.max(lastSlashIndex, lastBackSlashIndex); if (index >= 0) return filePath.substring(0, index); return null; } /** * Get the file name (and extension) from the gievn path.
* Example: file name for "\Temp\test\MyFile.txt" is "MyFile.txt". *

* Note: This method does not try to interpret the path, the last item of the path is assumed to be a file name (even if it is a * directory). So file name for "\Temp\test" is "test", and file name for "\Temp\" is "". *

* * @param filePath * a full file path */ public static String getFileName(String filePath) { if (filePath == null) return null; int lastSlashIndex = filePath.lastIndexOf('/'); int lastBackSlashIndex = filePath.lastIndexOf('\\'); int index = Math.max(lastSlashIndex, lastBackSlashIndex); if (index >= 0) return filePath.substring(index + 1); return filePath; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy