scolebourne 2004/11/22 16:04:29 Modified: io/src/test/org/apache/commons/io FilenameUtilsTestCase.java io/src/java/org/apache/commons/io FilenameUtils.java Log: Add methods to handle filename prefixes Revision Changes Path 1.20 +166 -6 jakarta-commons/io/src/test/org/apache/commons/io/FilenameUtilsTestCase.java Index: FilenameUtilsTestCase.java =================================================================== RCS file: /home/cvs/jakarta-commons/io/src/test/org/apache/commons/io/FilenameUtilsTestCase.java,v retrieving revision 1.19 retrieving revision 1.20 diff -u -r1.19 -r1.20 --- FilenameUtilsTestCase.java 31 Oct 2004 00:03:03 -0000 1.19 +++ FilenameUtilsTestCase.java 23 Nov 2004 00:04:29 -0000 1.20 @@ -113,6 +113,7 @@ public void testNormalize() throws Exception { String[] src = { + null, "", "/", "///", @@ -129,10 +130,14 @@ "/foo/.././bar/", "//foo//./bar", "/../", - "/foo/../../" }; + "/foo/../../", + "../foo", + "foo/../../bar", + "foo/../bar" }; String[] dest = { + null, "", "/", "/", @@ -149,15 +154,19 @@ "/bar/", "/foo/bar", null, - null }; + null, + null, + null, + "bar" }; assertEquals("Oops, test writer goofed", src.length, dest.length); for (int i = 0; i < src.length; i++) { + String destStr = FilenameUtils.separatorsToSystem(dest[i]); + String resultStr = FilenameUtils.normalize(src[i]); assertEquals( - "Check if '" + src[i] + "' normalized to '" + dest[i] + "'", - dest[i], - FilenameUtils.normalize(src[i])); + "Check if '" + src[i] + "' normalized to '" + destStr + "', was '" + resultStr + "'", + destStr, resultStr); } } @@ -214,6 +223,46 @@ } //----------------------------------------------------------------------- + public void testGetPrefixLength() { + assertEquals(-1, FilenameUtils.getPrefixLength(null)); + if (WINDOWS) { + assertEquals(-1, FilenameUtils.getPrefixLength("1:\\a\\b\\c.txt")); + assertEquals(-1, FilenameUtils.getPrefixLength("1:")); + assertEquals(-1, FilenameUtils.getPrefixLength("1:a")); + assertEquals(-1, FilenameUtils.getPrefixLength("\\\\\\a\\b\\c.txt")); + assertEquals(-1, FilenameUtils.getPrefixLength("\\\\a")); + + assertEquals(0, FilenameUtils.getPrefixLength("a\\b\\c.txt")); + assertEquals(1, FilenameUtils.getPrefixLength("\\a\\b\\c.txt")); + assertEquals(3, FilenameUtils.getPrefixLength("C:\\a\\b\\c.txt")); + assertEquals(9, FilenameUtils.getPrefixLength("\\\\server\\a\\b\\c.txt")); + + assertEquals(0, FilenameUtils.getPrefixLength("a/b/c.txt")); + assertEquals(1, FilenameUtils.getPrefixLength("/a/b/c.txt")); + assertEquals(3, FilenameUtils.getPrefixLength("C:/a/b/c.txt")); + assertEquals(9, FilenameUtils.getPrefixLength("//server/a/b/c.txt")); + + assertEquals(0, FilenameUtils.getPrefixLength("~/a/b/c.txt")); + assertEquals(0, FilenameUtils.getPrefixLength("~user/a/b/c.txt")); + } else { + assertEquals(-1, FilenameUtils.getPrefixLength("~")); + assertEquals(-1, FilenameUtils.getPrefixLength("~user")); + + assertEquals(0, FilenameUtils.getPrefixLength("a/b/c.txt")); + assertEquals(1, FilenameUtils.getPrefixLength("/a/b/c.txt")); + assertEquals(2, FilenameUtils.getPrefixLength("~/a/b/c.txt")); + assertEquals(6, FilenameUtils.getPrefixLength("~user/a/b/c.txt")); + + assertEquals(0, FilenameUtils.getPrefixLength("a\\b\\c.txt")); + assertEquals(1, FilenameUtils.getPrefixLength("\\a\\b\\c.txt")); + assertEquals(2, FilenameUtils.getPrefixLength("~\\a\\b\\c.txt")); + assertEquals(6, FilenameUtils.getPrefixLength("~user\\a\\b\\c.txt")); + + assertEquals(0, FilenameUtils.getPrefixLength("C:\\a\\b\\c.txt")); + assertEquals(1, FilenameUtils.getPrefixLength("\\\\server\\a\\b\\c.txt")); + } + } + public void testIndexOfLastSeparator() { assertEquals(-1, FilenameUtils.indexOfLastSeparator(null)); assertEquals(-1, FilenameUtils.indexOfLastSeparator("noseperator.inthispath")); @@ -233,6 +282,46 @@ } //----------------------------------------------------------------------- + public void testGetPrefix() { + assertEquals(null, FilenameUtils.getPrefix(null)); + if (WINDOWS) { + assertEquals(null, FilenameUtils.getPrefix("1:\\a\\b\\c.txt")); + assertEquals(null, FilenameUtils.getPrefix("1:")); + assertEquals(null, FilenameUtils.getPrefix("1:a")); + assertEquals(null, FilenameUtils.getPrefix("\\\\\\a\\b\\c.txt")); + assertEquals(null, FilenameUtils.getPrefix("\\\\a")); + + assertEquals("", FilenameUtils.getPrefix("a\\b\\c.txt")); + assertEquals("\\", FilenameUtils.getPrefix("\\a\\b\\c.txt")); + assertEquals("C:\\", FilenameUtils.getPrefix("C:\\a\\b\\c.txt")); + assertEquals("\\\\server\\", FilenameUtils.getPrefix("\\\\server\\a\\b\\c.txt")); + + assertEquals("", FilenameUtils.getPrefix("a/b/c.txt")); + assertEquals("/", FilenameUtils.getPrefix("/a/b/c.txt")); + assertEquals("C:/", FilenameUtils.getPrefix("C:/a/b/c.txt")); + assertEquals("//server/", FilenameUtils.getPrefix("//server/a/b/c.txt")); + + assertEquals("", FilenameUtils.getPrefix("~/a/b/c.txt")); + assertEquals("", FilenameUtils.getPrefix("~user/a/b/c.txt")); + } else { + assertEquals(null, FilenameUtils.getPrefix("~")); + assertEquals(null, FilenameUtils.getPrefix("~user")); + + assertEquals("", FilenameUtils.getPrefix("a/b/c.txt")); + assertEquals("/", FilenameUtils.getPrefix("/a/b/c.txt")); + assertEquals("~/", FilenameUtils.getPrefix("~/a/b/c.txt")); + assertEquals("~user/", FilenameUtils.getPrefix("~user/a/b/c.txt")); + + assertEquals("", FilenameUtils.getPrefix("a\\b\\c.txt")); + assertEquals("\\", FilenameUtils.getPrefix("\\a\\b\\c.txt")); + assertEquals("~\\", FilenameUtils.getPrefix("~\\a\\b\\c.txt")); + assertEquals("~user\\", FilenameUtils.getPrefix("~user\\a\\b\\c.txt")); + + assertEquals("", FilenameUtils.getPrefix("C:\\a\\b\\c.txt")); + assertEquals("\\", FilenameUtils.getPrefix("\\\\server\\a\\b\\c.txt")); + } + } + public void testGetPath() { assertEquals(null, FilenameUtils.getPath(null)); assertEquals("", FilenameUtils.getPath("noseperator.inthispath")); @@ -240,6 +329,67 @@ assertEquals("a/b", FilenameUtils.getPath("a/b/c")); assertEquals("a/b/c", FilenameUtils.getPath("a/b/c/")); assertEquals("a\\b", FilenameUtils.getPath("a\\b\\c")); + if (WINDOWS) { + assertEquals(null, FilenameUtils.getPath("1:/a/b/c.txt")); + assertEquals(null, FilenameUtils.getPath("1:")); + assertEquals(null, FilenameUtils.getPath("1:a")); + assertEquals(null, FilenameUtils.getPath("///a/b/c.txt")); + assertEquals(null, FilenameUtils.getPath("//a")); + + assertEquals("a/b", FilenameUtils.getPath("a/b/c.txt")); + assertEquals("a/b", FilenameUtils.getPath("/a/b/c.txt")); + assertEquals("a/b", FilenameUtils.getPath("C:/a/b/c.txt")); + assertEquals("a/b", FilenameUtils.getPath("//server/a/b/c.txt")); + + assertEquals("~/a/b", FilenameUtils.getPath("~/a/b/c.txt")); + assertEquals("~user/a/b", FilenameUtils.getPath("~user/a/b/c.txt")); + } else { + assertEquals(null, FilenameUtils.getPath("~")); + assertEquals(null, FilenameUtils.getPath("~user")); + + assertEquals("a/b", FilenameUtils.getPath("a/b/c.txt")); + assertEquals("a/b", FilenameUtils.getPath("/a/b/c.txt")); + assertEquals("a/b", FilenameUtils.getPath("~/a/b/c.txt")); + assertEquals("a/b", FilenameUtils.getPath("~user/a/b/c.txt")); + + assertEquals("C:/a/b", FilenameUtils.getPath("C:/a/b/c.txt")); + assertEquals("/server/a/b", FilenameUtils.getPath("//server/a/b/c.txt")); + } + } + + public void testGetFullPath() { + assertEquals(null, FilenameUtils.getFullPath(null)); + assertEquals("", FilenameUtils.getFullPath("noseperator.inthispath")); + assertEquals("a/b", FilenameUtils.getFullPath("a/b/c.txt")); + assertEquals("a/b", FilenameUtils.getFullPath("a/b/c")); + assertEquals("a/b/c", FilenameUtils.getFullPath("a/b/c/")); + assertEquals("a\\b", FilenameUtils.getFullPath("a\\b\\c")); + if (WINDOWS) { + assertEquals(null, FilenameUtils.getFullPath("1:/a/b/c.txt")); + assertEquals(null, FilenameUtils.getFullPath("1:")); + assertEquals(null, FilenameUtils.getFullPath("1:a")); + assertEquals(null, FilenameUtils.getFullPath("///a/b/c.txt")); + assertEquals(null, FilenameUtils.getFullPath("//a")); + + assertEquals("a/b", FilenameUtils.getFullPath("a/b/c.txt")); + assertEquals("/a/b", FilenameUtils.getFullPath("/a/b/c.txt")); + assertEquals("C:/a/b", FilenameUtils.getFullPath("C:/a/b/c.txt")); + assertEquals("//server/a/b", FilenameUtils.getFullPath("//server/a/b/c.txt")); + + assertEquals("~/a/b", FilenameUtils.getFullPath("~/a/b/c.txt")); + assertEquals("~user/a/b", FilenameUtils.getFullPath("~user/a/b/c.txt")); + } else { + assertEquals(null, FilenameUtils.getFullPath("~")); + assertEquals(null, FilenameUtils.getFullPath("~user")); + + assertEquals("a/b", FilenameUtils.getFullPath("a/b/c.txt")); + assertEquals("/a/b", FilenameUtils.getFullPath("/a/b/c.txt")); + assertEquals("~/a/b", FilenameUtils.getFullPath("~/a/b/c.txt")); + assertEquals("~user/a/b", FilenameUtils.getFullPath("~user/a/b/c.txt")); + + assertEquals("C:/a/b", FilenameUtils.getFullPath("C:/a/b/c.txt")); + assertEquals("//server/a/b", FilenameUtils.getFullPath("//server/a/b/c.txt")); + } } public void testGetName() { @@ -249,6 +399,16 @@ assertEquals("c", FilenameUtils.getName("a/b/c")); assertEquals("", FilenameUtils.getName("a/b/c/")); assertEquals("c", FilenameUtils.getName("a\\b\\c")); + } + + public void testGetBaseName() { + assertEquals(null, FilenameUtils.getBaseName(null)); + assertEquals("noseperator", FilenameUtils.getBaseName("noseperator.inthispath")); + assertEquals("c", FilenameUtils.getBaseName("a/b/c.txt")); + assertEquals("c", FilenameUtils.getBaseName("a/b/c")); + assertEquals("", FilenameUtils.getBaseName("a/b/c/")); + assertEquals("c", FilenameUtils.getBaseName("a\\b\\c")); + assertEquals("file.txt", FilenameUtils.getBaseName("file.txt.bak")); } public void testGetExtension() { 1.27 +270 -270 jakarta-commons/io/src/java/org/apache/commons/io/FilenameUtils.java Index: FilenameUtils.java =================================================================== RCS file: /home/cvs/jakarta-commons/io/src/java/org/apache/commons/io/FilenameUtils.java,v retrieving revision 1.26 retrieving revision 1.27 diff -u -r1.26 -r1.27 --- FilenameUtils.java 22 Nov 2004 01:11:55 -0000 1.26 +++ FilenameUtils.java 23 Nov 2004 00:04:29 -0000 1.27 @@ -23,37 +23,22 @@ /** * Utility class that provides methods to manipulate filenames and filepaths. * <p> - * This class defines four basic components within a filename (example C:\dev\project\file.txt): + * This class defines six components within a filename (example C:\dev\project\file.txt): * <ul> - * <li>the prefix - C:\ - * <li>the path - dev\project - * <li>the name - file.txt - * <li>the extension - txt + * <li>the prefix - C:\</li> + * <li>the path - dev\project</li> + * <li>the full path - C:\dev\project</li> + * <li>the name - file.txt</li> + * <li>the base name - file</li> + * <li>the extension - txt</li> * </ul> * The class only supports Unix and Windows style names. - * - * <h3>Path-related methods</h3> - * - * <p>Methods exist to retrieve the components of a typical file path. For - * example <code>/www/hosted/mysite/index.html</code>, can be broken into: - * <ul> - * <li><code>/www/hosted/mysite/</code> -- retrievable through - * [EMAIL PROTECTED] #getPath}</li> - * <li><code>index.html</code> -- retrievable through [EMAIL PROTECTED] #removePath}</li> - * <li><code>/www/hosted/mysite/index</code> -- retrievable through - * [EMAIL PROTECTED] #removeExtension}</li> - * <li><code>html</code> -- retrievable through [EMAIL PROTECTED] #getExtension}</li> - * </ul> - * There are also methods to [EMAIL PROTECTED] #catPath concatenate two paths}, - * [EMAIL PROTECTED] #resolveFile resolve a path relative to a File} and - * [EMAIL PROTECTED] #normalize} a path. * </p> - * * <h3>Origin of code</h3> * <ul> - * <li>commons-utils repo</li> - * <li>Alexandria's FileUtils.</li> - * <li>Avalon Excalibur's IO.</li> + * <li>Commons Utils</li> + * <li>Alexandria's FileUtils</li> + * <li>Avalon Excalibur's IO</li> * </ul> * * @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</A> @@ -91,125 +76,47 @@ */ private static final char SYSTEM_SEPARATOR = File.separatorChar; -// /** -// * The separator character that is the opposite of the system separator. -// */ -// private static final char OTHER_SEPARATOR; -// static { -// if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) { -// OTHER_SEPARATOR = UNIX_SEPARATOR; -// } else { -// OTHER_SEPARATOR = WINDOWS_SEPARATOR; -// } -// } + /** + * The separator character that is the opposite of the system separator. + */ + private static final char OTHER_SEPARATOR; + static { + if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) { + OTHER_SEPARATOR = UNIX_SEPARATOR; + } else { + OTHER_SEPARATOR = WINDOWS_SEPARATOR; + } + } /** * Instances should NOT be constructed in standard programming. */ public FilenameUtils() { } -// //----------------------------------------------------------------------- -// /** -// * Checks if the character is a separator. -// * -// * @param ch the character to check -// * @return true if it is a separators -// */ -// private static boolean isSeparator(char ch) { -// return (ch == UNIX_SEPARATOR) || (ch == WINDOWS_SEPARATOR); -// } -// -// //----------------------------------------------------------------------- -// /** -// * Normalizes a path, removing double and single dot path steps. -// * <p> -// * This method normalizes a path to a standard format. -// * The input may contain separators in either Unix or Windows format. -// * The output will contain separators in the format of the system. -// * <p> -// * A double slash will be merged to a single slash (thus UNC names are not handled). -// * A single dot path segment will be removed with no other effect. -// * A double dot will cause that path segment and the one before to be removed. -// * If the double dot has no parent path segment to work with, <code>null</code> -// * is returned. -// * <pre> -// * /foo// --> /foo/ -// * /foo/./ --> /foo/ -// * /foo/../bar --> /bar -// * /foo/../bar/ --> /bar/ -// * /foo/../bar/../baz --> /baz -// * //foo//./bar --> /foo/bar -// * /../ --> null -// * ../foo --> null -// * foo/../../bar --> null -// * foo/../bar --> bar -// * </pre> -// * -// * @param path the path to normalize, null returns null -// * @return the normalized String, or null if too many ..'s. -// * @todo prefixes for Windows -// */ -// public static String normalize(String path) { -// if (path == null) { -// return null; -// } -// char[] array = path.toCharArray(); -// int size = array.length; -// // fix separators -// for (int i = 0; i < array.length; i++) { -// if (array[i] == OTHER_SEPARATOR) { -// array[i] = SYSTEM_SEPARATOR; -// } -// } -// // adjoining slashes -// for (int i = 1; i < size; i++) { -// if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == SYSTEM_SEPARATOR) { -// System.arraycopy(array, i, array, i - 1, size - i); -// size--; -// i--; -// } -// } -// // dot slash -// for (int i = 2; i < size; i++) { -// if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' && -// array[i - 2] == SYSTEM_SEPARATOR) { -// System.arraycopy(array, i, array, i - 2, size - i); -// size -=2; -// i--; -// } -// } -// // double dot slash -// outer: -// for (int i = 2; i < size; i++) { -// if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' && -// array[i - 2] == '.' && (i == 2 || array[i - 3] == SYSTEM_SEPARATOR)) { -// if (i == 2) { -// return null; -// } -// int j; -// for (j = i - 4 ; j >= 0; j--) { -// if (array[j] == SYSTEM_SEPARATOR) { -// System.arraycopy(array, i, array, j, size - i); -// size -= (i - j); -// i = j + 1; -// continue outer; -// } -// } -// System.arraycopy(array, i + 1, array, 0, size - i - 1); -// size -= (i + 1); -// i = 1; -// } -// } -// -// return new String(array, 0, size); -// } + //----------------------------------------------------------------------- + /** + * Checks if the character is a separator. + * + * @param ch the character to check + * @return true if it is a separators + */ + private static boolean isSeparator(char ch) { + return (ch == UNIX_SEPARATOR) || (ch == WINDOWS_SEPARATOR); + } //----------------------------------------------------------------------- /** - * Normalize a path. - * Eliminates "/../" and "/./" in a string. Returns <code>null</code> if - * the ..'s went past the root. - * Eg: + * Normalizes a path, removing double and single dot path steps. + * <p> + * This method normalizes a path to a standard format. + * The input may contain separators in either Unix or Windows format. + * The output will contain separators in the format of the system. + * <p> + * A double slash will be merged to a single slash (thus UNC names are not handled). + * A single dot path segment will be removed with no other effect. + * A double dot will cause that path segment and the one before to be removed. + * If the double dot has no parent path segment to work with, <code>null</code> + * is returned. * <pre> * /foo// --> /foo/ * /foo/./ --> /foo/ @@ -218,53 +125,74 @@ * /foo/../bar/../baz --> /baz * //foo//./bar --> /foo/bar * /../ --> null + * ../foo --> null + * foo/../../bar --> null + * foo/../bar --> bar * </pre> * - * @param path the path to normalize - * @return the normalized String, or <code>null</code> if too many ..'s. - * @todo Make this non-unix specific - */ - public static String normalize(String path) { - String normalized = path; - // Resolve occurrences of "//" in the normalized path - while (true) { - int index = normalized.indexOf("//"); - if (index < 0) { - break; + * @param filename the filename to normalize, null returns null + * @return the normalized String, or null if too many ..'s. + */ + public static String normalize(String filename) { + if (filename == null) { + return null; + } + char[] array = filename.toCharArray(); + int prefix = getPrefixLength(filename); + if (prefix < 0) { + return null; + } + + // TODO: Use prefix + + int size = array.length; + // fix separators + for (int i = 0; i < array.length; i++) { + if (array[i] == OTHER_SEPARATOR) { + array[i] = SYSTEM_SEPARATOR; } - normalized = - normalized.substring(0, index) - + normalized.substring(index + 1); } - - // Resolve occurrences of "/./" in the normalized path - while (true) { - int index = normalized.indexOf("/./"); - if (index < 0) { - break; + // adjoining slashes + for (int i = 1; i < size; i++) { + if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == SYSTEM_SEPARATOR) { + System.arraycopy(array, i, array, i - 1, size - i); + size--; + i--; } - normalized = - normalized.substring(0, index) - + normalized.substring(index + 2); } - - // Resolve occurrences of "/../" in the normalized path - while (true) { - int index = normalized.indexOf("/../"); - if (index < 0) { - break; + // dot slash + for (int i = 2; i < size; i++) { + if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' && + array[i - 2] == SYSTEM_SEPARATOR) { + System.arraycopy(array, i, array, i - 2, size - i); + size -=2; + i--; } - if (index == 0) { - return null; // Trying to go outside our context + } + // double dot slash + outer: + for (int i = 2; i < size; i++) { + if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' && + array[i - 2] == '.' && (i == 2 || array[i - 3] == SYSTEM_SEPARATOR)) { + if (i == 2) { + return null; + } + int j; + for (j = i - 4 ; j >= 0; j--) { + if (array[j] == SYSTEM_SEPARATOR) { + System.arraycopy(array, i, array, j, size - i); + size -= (i - j); + i = j + 1; + continue outer; + } + } + System.arraycopy(array, i + 1, array, 0, size - i - 1); + size -= (i + 1); + i = 1; } - int index2 = normalized.lastIndexOf('/', index - 1); - normalized = - normalized.substring(0, index2) - + normalized.substring(index + 3); } - - // Return the normalized path that we have completed - return normalized; + + return new String(array, 0, size); } /** @@ -431,61 +359,81 @@ } //----------------------------------------------------------------------- -// /** -// * Returns the length of the filename prefix, such as <code>C:/</code> or <code>~/</code>. -// * <p> -// * This method will handle a file in either Unix or Windows format. -// * The prefix includes the first slash in the full filename. -// * <pre> -// * Windows: -// * a\b\c.txt --> "" --> relative -// * \a\b\c.txt --> "\" --> drive relative -// * C:\a\b\c.txt --> "C:\" --> absolute -// * \\server\a\b\c.txt --> "\\server\" --> UNC -// * -// * Unix: -// * a/b/c.txt --> "" --> relative -// * /a/b/c.txt --> "/" --> absolute -// * ~/a/b/c.txt --> "~/" --> current user relative -// * ~user/a/b/c.txt --> "~user/" --> named user relative -// * </pre> -// * -// * @param filename the filename to find the prefix in, null returns -1 -// * @return the length of the prefix, -1 if invalid or null -// */ -// public static int getPrefixLength(String filename) { -// if (filename == null) { -// return -1; -// } -// int len = filename.length(); -// if (len == 0) { -// return 0; -// } -// if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) { -// char ch0 = filename.charAt(0); -// if (len == 1) { -// return (isSeparator(ch0) ? 1 : 0); -// } else { -// char ch1 = filename.charAt(1); -// if (ch1 == ':') { -// ch0 = Character.toUpperCase(ch0); -// if (ch0 < 'A' || ch0 > 'Z' || len == 2 || isSeparator(filename.charAt(2)) == false) { -// return -1; -// } -// return 3; -// } else if (isSeparator(ch0) && isSeparator(ch1)) { -// int pos = Math.min( -// filename.indexOf(UNIX_SEPARATOR, 2), -// filename.indexOf(WINDOWS_SEPARATOR, 2)); -// return (pos == -1 || pos == 2 ? -1 : pos + 1); -// } else { -// return (isSeparator(ch0) ? 1 : 0); -// } -// } -// } else { -// } -// return 0; -// } + /** + * Returns the length of the filename prefix, such as <code>C:/</code> or <code>~/</code>. + * <p> + * This method will handle a file in either Unix or Windows format. + * The prefix includes the first slash in the full filename. + * <pre> + * Windows: + * a\b\c.txt --> "" --> relative + * \a\b\c.txt --> "\" --> drive relative + * C:\a\b\c.txt --> "C:\" --> absolute + * \\server\a\b\c.txt --> "\\server\" --> UNC + * + * Unix: + * a/b/c.txt --> "" --> relative + * /a/b/c.txt --> "/" --> absolute + * ~/a/b/c.txt --> "~/" --> current user relative + * ~user/a/b/c.txt --> "~user/" --> named user relative + * </pre> + * + * @param filename the filename to find the prefix in, null returns -1 + * @return the length of the prefix, -1 if invalid or null + */ + public static int getPrefixLength(String filename) { + if (filename == null) { + return -1; + } + int len = filename.length(); + if (len == 0) { + return 0; + } + if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) { + char ch0 = filename.charAt(0); + if (len == 1) { + return (isSeparator(ch0) ? 1 : 0); + } else { + char ch1 = filename.charAt(1); + if (ch1 == ':') { + ch0 = Character.toUpperCase(ch0); + if (ch0 < 'A' || ch0 > 'Z' || len == 2 || isSeparator(filename.charAt(2)) == false) { + return -1; + } + return 3; + } else if (isSeparator(ch0) && isSeparator(ch1)) { + int posUnix = filename.indexOf(UNIX_SEPARATOR, 2); + int posWin = filename.indexOf(WINDOWS_SEPARATOR, 2); + if ((posUnix == -1 && posWin == -1) || posUnix == 2 || posWin == 2) { + return -1; + } + posUnix = (posUnix == -1 ? posWin : posUnix); + posWin = (posWin == -1 ? posUnix : posWin); + return Math.min(posUnix, posWin) + 1; + } else { + return (isSeparator(ch0) ? 1 : 0); + } + } + } else { + char ch0 = filename.charAt(0); + char ch1 = filename.charAt(1); + if (ch0 == '~') { + if (len == 1) { + return -1; + } + int posUnix = filename.indexOf(UNIX_SEPARATOR, 1); + int posWin = filename.indexOf(WINDOWS_SEPARATOR, 1); + if (posUnix == -1 && posWin == -1) { + return -1; + } + posUnix = (posUnix == -1 ? posWin : posUnix); + posWin = (posWin == -1 ? posUnix : posWin); + return Math.min(posUnix, posWin) + 1; + } else { + return (isSeparator(ch0) ? 1 : 0); + } + } + } /** * Returns the index of the last directory separator character. @@ -527,61 +475,95 @@ } //----------------------------------------------------------------------- -// /** -// * Gets the prefix from a full filename, such as <code>C:/</code> or <code>~/</code>. -// * <p> -// * This method will handle a file in either Unix or Windows format. -// * The prefix includes the first slash in the full filename. -// * <pre> -// * Windows: -// * a\b\c.txt --> "" --> relative -// * \a\b\c.txt --> "\" --> drive relative -// * C:\a\b\c.txt --> "C:\" --> absolute -// * \\server\a\b\c.txt --> "\\server\" --> UNC -// * -// * Unix: -// * a/b/c.txt --> "" --> relative -// * /a/b/c.txt --> "/" --> absolute -// * ~/a/b/c.txt --> "~/" --> current user relative -// * ~user/a/b/c.txt --> "~user/" --> named user relative -// * </pre> -// * -// * @param filename the filename to query, null returns null -// * @return the prefix of the file, null if invalid -// */ -// public static String getPrefix(String filename) { -// if (filename == null) { -// return null; -// } -// int len = getPrefixLength(filename); -// if (len < 0) { -// return null; -// } -// return filename.substring(0, len); -// } + /** + * Gets the prefix from a full filename, such as <code>C:/</code> or <code>~/</code>. + * <p> + * This method will handle a file in either Unix or Windows format. + * The prefix includes the first slash in the full filename. + * <pre> + * Windows: + * a\b\c.txt --> "" --> relative + * \a\b\c.txt --> "\" --> drive relative + * C:\a\b\c.txt --> "C:\" --> absolute + * \\server\a\b\c.txt --> "\\server\" --> UNC + * + * Unix: + * a/b/c.txt --> "" --> relative + * /a/b/c.txt --> "/" --> absolute + * ~/a/b/c.txt --> "~/" --> current user relative + * ~user/a/b/c.txt --> "~user/" --> named user relative + * </pre> + * + * @param filename the filename to query, null returns null + * @return the prefix of the file, null if invalid + */ + public static String getPrefix(String filename) { + if (filename == null) { + return null; + } + int len = getPrefixLength(filename); + if (len < 0) { + return null; + } + return filename.substring(0, len); + } /** - * Gets the path from a full filename. + * Gets the path from a full filename, which excludes the prefix. * <p> * This method will handle a file in either Unix or Windows format. * The text before the last forward or backslash is returned. - * This method is roughly equivalent to the unix command <code>dirname</code>. * <pre> - * a/b/c.txt --> a/b - * a.txt --> "" - * a/b/c --> a/b - * a/b/c/ --> a/b/c + * ~/a/b/c.txt --> a/b + * a.txt --> "" + * a/b/c --> a/b + * a/b/c/ --> a/b/c * </pre> * * @param filename the filename to query, null returns null - * @return the path of the file, or an empty string if none exists + * @return the path of the file, an empty string if none exists, null if invalid */ public static String getPath(String filename) { if (filename == null) { return null; } + int prefix = getPrefixLength(filename); + if (prefix < 0) { + return null; + } + int index = indexOfLastSeparator(filename); + if (index < 0) { + return ""; + } else { + return filename.substring(prefix, index); + } + } + + /** + * Gets the full path from a full filename, which is the prefix + path. + * <p> + * This method will handle a file in either Unix or Windows format. + * The text before the last forward or backslash is returned. + * <pre> + * ~/a/b/c.txt --> ~/a/b + * a.txt --> "" + * a/b/c --> a/b + * a/b/c/ --> a/b/c + * </pre> + * + * @param filename the filename to query, null returns null + * @return the path of the file, an empty string if none exists, null if invalid + */ + public static String getFullPath(String filename) { + if (filename == null) { + return null; + } + int prefix = getPrefixLength(filename); // validate the prefix + if (prefix < 0) { + return null; + } int index = indexOfLastSeparator(filename); - if (index == -1) { + if (index < 0) { return ""; } else { return filename.substring(0, index); @@ -593,7 +575,6 @@ * <p> * This method will handle a file in either Unix or Windows format. * The text after the last forward or backslash is returned. - * This method is roughly equivalent to the unix command <code>basename</code>. * <pre> * a/b/c.txt --> c.txt * a.txt --> a.txt @@ -610,6 +591,25 @@ } int index = indexOfLastSeparator(filename); return filename.substring(index + 1); + } + + /** + * Gets the base name, minus the full path and extension, from a full filename. + * <p> + * This method will handle a file in either Unix or Windows format. + * The text after the last forward or backslash and before the last dot is returned. + * <pre> + * a/b/c.txt --> c + * a.txt --> a + * a/b/c --> c + * a/b/c/ --> "" + * </pre> + * + * @param filename the filename to query, null returns null + * @return the name of the file without the path, or an empty string if none exists + */ + public static String getBaseName(String filename) { + return removeExtension(getName(filename)); } /**
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]