KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jodd > io > FileNameUtil


1 // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
2

3 package jodd.io;
4
5 import java.io.File JavaDoc;
6
7 /**
8  * General filename and filepath manipulation utilities.
9  * <p>
10  * When dealing with filenames you can hit problems when moving from a Windows
11  * based development machine to a Unix based production machine.
12  * This class aims to help avoid those problems.
13  * <p>
14  * <b>NOTE</b>: You may be able to avoid using this class entirely simply by
15  * using JDK {@link java.io.File File} objects and the two argument constructor
16  * {@link java.io.File#File(java.io.File, java.lang.String) File(File,String)}.
17  * <p>
18  * Most methods on this class are designed to work the same on both Unix and Windows.
19  * Those that don't include 'System', 'Unix' or 'Windows' in their name.
20  * <p>
21  * Most methods recognise both separators (forward and back), and both
22  * sets of prefixes. See the javadoc of each method for details.
23  * <p>
24  * This class defines six components within a filename
25  * (example C:\dev\project\file.txt):
26  * <ul>
27  * <li>the prefix - C:\</li>
28  * <li>the path - dev\project\</li>
29  * <li>the full path - C:\dev\project\</li>
30  * <li>the name - file.txt</li>
31  * <li>the base name - file</li>
32  * <li>the extension - txt</li>
33  * </ul>
34  * Note that this class works best if directory filenames end with a separator.
35  * If you omit the last separator, it is impossible to determine if the filename
36  * corresponds to a file or a directory. As a result, we have chosen to say
37  * it corresponds to a file.
38  * <p>
39  * This class only supports Unix and Windows style names.
40  * Prefixes are matched as follows:
41  * <pre>
42  * Windows:
43  * a\b\c.txt --> "" --> relative
44  * \a\b\c.txt --> "\" --> current drive absolute
45  * C:a\b\c.txt --> "C:" --> drive relative
46  * C:\a\b\c.txt --> "C:\" --> absolute
47  * \\server\a\b\c.txt --> "\\server\" --> UNC
48  *
49  * Unix:
50  * a/b/c.txt --> "" --> relative
51  * /a/b/c.txt --> "/" --> absolute
52  * ~/a/b/c.txt --> "~/" --> current user
53  * ~ --> "~/" --> current user (slash added)
54  * ~user/a/b/c.txt --> "~user/" --> named user
55  * ~user --> "~user/" --> named user (slash added)
56  * </pre>
57  * Both prefix styles are matched always, irrespective of the machine that you are
58  * currently running on.
59  */

60 public class FileNameUtil {
61
62     /**
63      * The extension separator character.
64      */

65     private static final char EXTENSION_SEPARATOR = '.';
66
67     /**
68      * The Unix separator character.
69      */

70     private static final char UNIX_SEPARATOR = '/';
71
72     /**
73      * The Windows separator character.
74      */

75     private static final char WINDOWS_SEPARATOR = '\\';
76
77     /**
78      * The system separator character.
79      */

80     private static final char SYSTEM_SEPARATOR = File.separatorChar;
81
82     /**
83      * The separator character that is the opposite of the system separator.
84      */

85     private static final char OTHER_SEPARATOR;
86     static {
87         if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) {
88             OTHER_SEPARATOR = UNIX_SEPARATOR;
89         } else {
90             OTHER_SEPARATOR = WINDOWS_SEPARATOR;
91         }
92     }
93
94     /**
95      * Checks if the character is a separator.
96      */

97     private static boolean isSeparator(char ch) {
98         return (ch == UNIX_SEPARATOR) || (ch == WINDOWS_SEPARATOR);
99     }
100
101     // ---------------------------------------------------------------- normalization
102
/**
103      * Normalizes a path, removing double and single dot path steps.
104      * <p>
105      * This method normalizes a path to a standard format.
106      * The input may contain separators in either Unix or Windows format.
107      * The output will contain separators in the format of the system.
108      * <p>
109      * A trailing slash will be retained.
110      * A double slash will be merged to a single slash (but UNC names are handled).
111      * A single dot path segment will be removed.
112      * A double dot will cause that path segment and the one before to be removed.
113      * If the double dot has no parent path segment to work with, <code>null</code>
114      * is returned.
115      * <p>
116      * The output will be the same on both Unix and Windows except
117      * for the separator character.
118      * <pre>
119      * /foo// --> /foo/
120      * /foo/./ --> /foo/
121      * /foo/../bar --> /bar
122      * /foo/../bar/ --> /bar/
123      * /foo/../bar/../baz --> /baz
124      * //foo//./bar --> /foo/bar
125      * /../ --> null
126      * ../foo --> null
127      * foo/bar/.. --> foo/
128      * foo/../../bar --> null
129      * foo/../bar --> bar
130      * //server/foo/../bar --> //server/bar
131      * //server/../bar --> null
132      * C:\foo\..\bar --> C:\bar
133      * C:\..\bar --> null
134      * ~/foo/../bar/ --> ~/bar/
135      * ~/../bar --> null
136      * </pre>
137      * (Note the file separator returned will be correct for Windows/Unix)
138      *
139      * @param filename the filename to normalize, null returns null
140      * @return the normalized filename, or null if invalid
141      */

142     public static String JavaDoc normalize(String JavaDoc filename) {
143         return doNormalize(filename, true);
144     }
145
146
147
148     /**
149      * Normalizes a path, removing double and single dot path steps,
150      * and removing any final directory separator.
151      * <p>
152      * This method normalizes a path to a standard format.
153      * The input may contain separators in either Unix or Windows format.
154      * The output will contain separators in the format of the system.
155      * <p>
156      * A trailing slash will be removed.
157      * A double slash will be merged to a single slash (but UNC names are handled).
158      * A single dot path segment will be removed.
159      * A double dot will cause that path segment and the one before to be removed.
160      * If the double dot has no parent path segment to work with, <code>null</code>
161      * is returned.
162      * <p>
163      * The output will be the same on both Unix and Windows except
164      * for the separator character.
165      * <pre>
166      * /foo// --> /foo
167      * /foo/./ --> /foo
168      * /foo/../bar --> /bar
169      * /foo/../bar/ --> /bar
170      * /foo/../bar/../baz --> /baz
171      * //foo//./bar --> /foo/bar
172      * /../ --> null
173      * ../foo --> null
174      * foo/bar/.. --> foo
175      * foo/../../bar --> null
176      * foo/../bar --> bar
177      * //server/foo/../bar --> //server/bar
178      * //server/../bar --> null
179      * C:\foo\..\bar --> C:\bar
180      * C:\..\bar --> null
181      * ~/foo/../bar/ --> ~/bar
182      * ~/../bar --> null
183      * </pre>
184      * (Note the file separator returned will be correct for Windows/Unix)
185      *
186      * @param filename the filename to normalize, null returns null
187      * @return the normalized filename, or null if invalid
188      */

189     public static String JavaDoc normalizeNoEndSeparator(String JavaDoc filename) {
190         return doNormalize(filename, false);
191     }
192
193     /**
194      * Internal method to perform the normalization.
195      *
196      * @param filename the filename
197      * @param keepSeparator true to keep the final separator
198      * @return the normalized filename
199      */

200     private static String JavaDoc doNormalize(String JavaDoc filename, boolean keepSeparator) {
201         if (filename == null) {
202             return null;
203         }
204         int size = filename.length();
205         if (size == 0) {
206             return filename;
207         }
208         int prefix = getPrefixLength(filename);
209         if (prefix < 0) {
210             return null;
211         }
212
213         char[] array = new char[size + 2]; // +1 for possible extra slash, +2 for arraycopy
214
filename.getChars(0, filename.length(), array, 0);
215
216         // fix separators throughout
217
for (int i = 0; i < array.length; i++) {
218             if (array[i] == OTHER_SEPARATOR) {
219                 array[i] = SYSTEM_SEPARATOR;
220             }
221         }
222
223         // add extra separator on the end to simplify code below
224
boolean lastIsDirectory = true;
225         if (array[size - 1] != SYSTEM_SEPARATOR) {
226             array[size] = SYSTEM_SEPARATOR;
227             size++;
228             lastIsDirectory = false;
229         }
230
231         // adjoining slashes
232
for (int i = prefix + 1; i < size; i++) {
233             if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == SYSTEM_SEPARATOR) {
234                 System.arraycopy(array, i, array, i - 1, size - i);
235                 size--;
236                 i--;
237             }
238         }
239
240         // dot slash
241
for (int i = prefix + 1; i < size; i++) {
242             if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' &&
243                     (i == prefix + 1 || array[i - 2] == SYSTEM_SEPARATOR)) {
244                 if (i == size - 1) {
245                     lastIsDirectory = true;
246                 }
247                 System.arraycopy(array, i + 1, array, i - 1, size - i);
248                 size -=2;
249                 i--;
250             }
251         }
252
253         // double dot slash
254
outer:
255         for (int i = prefix + 2; i < size; i++) {
256             if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == '.' && array[i - 2] == '.' &&
257                     (i == prefix + 2 || array[i - 3] == SYSTEM_SEPARATOR)) {
258                 if (i == prefix + 2) {
259                     return null;
260                 }
261                 if (i == size - 1) {
262                     lastIsDirectory = true;
263                 }
264                 int j;
265                 for (j = i - 4 ; j >= prefix; j--) {
266                     if (array[j] == SYSTEM_SEPARATOR) {
267                         // remove b/../ from a/b/../c
268
System.arraycopy(array, i + 1, array, j + 1, size - i);
269                         size -= (i - j);
270                         i = j + 1;
271                         continue outer;
272                     }
273                 }
274                 // remove a/../ from a/../c
275
System.arraycopy(array, i + 1, array, prefix, size - i);
276                 size -= (i + 1 - prefix);
277                 i = prefix + 1;
278             }
279         }
280
281         if (size <= 0) { // should never be less than 0
282
return "";
283         }
284         if (size <= prefix) { // should never be less than prefix
285
return new String JavaDoc(array, 0, size);
286         }
287         if (lastIsDirectory && keepSeparator) {
288             return new String JavaDoc(array, 0, size); // keep trailing separator
289
}
290         return new String JavaDoc(array, 0, size - 1); // lose trailing separator
291
}
292
293     //-----------------------------------------------------------------------
294
/**
295      * Concatenates a filename to a base path using normal command line style rules.
296      * <p>
297      * The effect is equivalent to resultant directory after changing
298      * directory to the first argument, followed by changing directory to
299      * the second argument.
300      * <p>
301      * The first argument is the base path, the second is the path to concatenate.
302      * The returned path is always normalized via {@link #normalize(String)},
303      * thus <code>..</code> is handled.
304      * <p>
305      * If <code>pathToAdd</code> is absolute (has an absolute prefix), then
306      * it will be normalized and returned.
307      * Otherwise, the paths will be joined, normalized and returned.
308      * <p>
309      * The output will be the same on both Unix and Windows except
310      * for the separator character.
311      * <pre>
312      * /foo/ + bar --> /foo/bar
313      * /foo + bar --> /foo/bar
314      * /foo + /bar --> /bar
315      * /foo + C:/bar --> C:/bar
316      * /foo + C:bar --> C:bar (*)
317      * /foo/a/ + ../bar --> foo/bar
318      * /foo/ + ../../bar --> null
319      * /foo/ + /bar --> /bar
320      * /foo/.. + /bar --> /bar
321      * /foo + bar/c.txt --> /foo/bar/c.txt
322      * /foo/c.txt + bar --> /foo/c.txt/bar (!)
323      * </pre>
324      * (*) Note that the Windows relative drive prefix is unreliable when
325      * used with this method.
326      * (!) Note that the first parameter must be a path. If it ends with a name, then
327      * the name will be built into the concatenated path. If this might be a problem,
328      * use {@link #getFullPath(String)} on the base path argument.
329      *
330      * @param basePath the base path to attach to, always treated as a path
331      * @param fullFilenameToAdd the filename (or path) to attach to the base
332      * @return the concatenated path, or null if invalid
333      */

334     public static String JavaDoc concat(String JavaDoc basePath, String JavaDoc fullFilenameToAdd) {
335         int prefix = getPrefixLength(fullFilenameToAdd);
336         if (prefix < 0) {
337             return null;
338         }
339         if (prefix > 0) {
340             return normalize(fullFilenameToAdd);
341         }
342         if (basePath == null) {
343             return null;
344         }
345         int len = basePath.length();
346         if (len == 0) {
347             return normalize(fullFilenameToAdd);
348         }
349         char ch = basePath.charAt(len - 1);
350         if (isSeparator(ch)) {
351             return normalize(basePath + fullFilenameToAdd);
352         } else {
353             return normalize(basePath + '/' + fullFilenameToAdd);
354         }
355     }
356
357     // ---------------------------------------------------------------- separator conversion
358

359     /**
360      * Converts all separators to the Unix separator of forward slash.
361      *
362      * @param path the path to be changed, null ignored
363      * @return the updated path
364      */

365     public static String JavaDoc separatorsToUnix(String JavaDoc path) {
366         if (path == null || path.indexOf(WINDOWS_SEPARATOR) == -1) {
367             return path;
368         }
369         return path.replace(WINDOWS_SEPARATOR, UNIX_SEPARATOR);
370     }
371
372     /**
373      * Converts all separators to the Windows separator of backslash.
374      *
375      * @param path the path to be changed, null ignored
376      * @return the updated path
377      */

378     public static String JavaDoc separatorsToWindows(String JavaDoc path) {
379         if (path == null || path.indexOf(UNIX_SEPARATOR) == -1) {
380             return path;
381         }
382         return path.replace(UNIX_SEPARATOR, WINDOWS_SEPARATOR);
383     }
384
385     /**
386      * Converts all separators to the system separator.
387      *
388      * @param path the path to be changed, null ignored
389      * @return the updated path
390      */

391     public static String JavaDoc separatorsToSystem(String JavaDoc path) {
392         if (path == null) {
393             return null;
394         }
395         if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) {
396             return separatorsToWindows(path);
397         } else {
398             return separatorsToUnix(path);
399         }
400     }
401
402     // ---------------------------------------------------------------- prefix
403
/**
404      * Returns the length of the filename prefix, such as <code>C:/</code> or <code>~/</code>.
405      * <p>
406      * This method will handle a file in either Unix or Windows format.
407      * <p>
408      * The prefix length includes the first slash in the full filename
409      * if applicable. Thus, it is possible that the length returned is greater
410      * than the length of the input string.
411      * <pre>
412      * Windows:
413      * a\b\c.txt --> "" --> relative
414      * \a\b\c.txt --> "\" --> current drive absolute
415      * C:a\b\c.txt --> "C:" --> drive relative
416      * C:\a\b\c.txt --> "C:\" --> absolute
417      * \\server\a\b\c.txt --> "\\server\" --> UNC
418      *
419      * Unix:
420      * a/b/c.txt --> "" --> relative
421      * /a/b/c.txt --> "/" --> absolute
422      * ~/a/b/c.txt --> "~/" --> current user
423      * ~ --> "~/" --> current user (slash added)
424      * ~user/a/b/c.txt --> "~user/" --> named user
425      * ~user --> "~user/" --> named user (slash added)
426      * </pre>
427      * <p>
428      * The output will be the same irrespective of the machine that the code is running on.
429      * ie. both Unix and Windows prefixes are matched regardless.
430      *
431      * @param filename the filename to find the prefix in, null returns -1
432      * @return the length of the prefix, -1 if invalid or null
433      */

434     public static int getPrefixLength(String JavaDoc filename) {
435         if (filename == null) {
436             return -1;
437         }
438         int len = filename.length();
439         if (len == 0) {
440             return 0;
441         }
442         char ch0 = filename.charAt(0);
443         if (ch0 == ':') {
444             return -1;
445         }
446         if (len == 1) {
447             if (ch0 == '~') {
448                 return 2; // return a length greater than the input
449
}
450             return (isSeparator(ch0) ? 1 : 0);
451         } else {
452             if (ch0 == '~') {
453                 int posUnix = filename.indexOf(UNIX_SEPARATOR, 1);
454                 int posWin = filename.indexOf(WINDOWS_SEPARATOR, 1);
455                 if (posUnix == -1 && posWin == -1) {
456                     return len + 1; // return a length greater than the input
457
}
458                 posUnix = (posUnix == -1 ? posWin : posUnix);
459                 posWin = (posWin == -1 ? posUnix : posWin);
460                 return Math.min(posUnix, posWin) + 1;
461             }
462             char ch1 = filename.charAt(1);
463             if (ch1 == ':') {
464                 ch0 = Character.toUpperCase(ch0);
465                 if (ch0 >= 'A' && ch0 <= 'Z') {
466                     if (len == 2 || isSeparator(filename.charAt(2)) == false) {
467                         return 2;
468                     }
469                     return 3;
470                 }
471                 return -1;
472
473             } else if (isSeparator(ch0) && isSeparator(ch1)) {
474                 int posUnix = filename.indexOf(UNIX_SEPARATOR, 2);
475                 int posWin = filename.indexOf(WINDOWS_SEPARATOR, 2);
476                 if ((posUnix == -1 && posWin == -1) || posUnix == 2 || posWin == 2) {
477                     return -1;
478                 }
479                 posUnix = (posUnix == -1 ? posWin : posUnix);
480                 posWin = (posWin == -1 ? posUnix : posWin);
481                 return Math.min(posUnix, posWin) + 1;
482             } else {
483                 return (isSeparator(ch0) ? 1 : 0);
484             }
485         }
486     }
487
488     /**
489      * Returns the index of the last directory separator character.
490      * <p>
491      * This method will handle a file in either Unix or Windows format.
492      * The position of the last forward or backslash is returned.
493      * <p>
494      * The output will be the same irrespective of the machine that the code is running on.
495      *
496      * @param filename the filename to find the last path separator in, null returns -1
497      * @return the index of the last separator character, or -1 if there is no such character
498      */

499     public static int indexOfLastSeparator(String JavaDoc filename) {
500         if (filename == null) {
501             return -1;
502         }
503         int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);
504         int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
505         return Math.max(lastUnixPos, lastWindowsPos);
506     }
507
508     /**
509      * Returns the index of the last extension separator character, which is a dot.
510      * <p>
511      * This method also checks that there is no directory separator after the last dot.
512      * To do this it uses {@link #indexOfLastSeparator(String)} which will
513      * handle a file in either Unix or Windows format.
514      * <p>
515      * The output will be the same irrespective of the machine that the code is running on.
516      *
517      * @param filename the filename to find the last path separator in, null returns -1
518      * @return the index of the last separator character, or -1 if there
519      * is no such character
520      */

521     public static int indexOfExtension(String JavaDoc filename) {
522         if (filename == null) {
523             return -1;
524         }
525         int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
526         int lastSeparator = indexOfLastSeparator(filename);
527         return (lastSeparator > extensionPos ? -1 : extensionPos);
528     }
529
530     // ---------------------------------------------------------------- get
531

532     /**
533      * Gets the prefix from a full filename, such as <code>C:/</code>
534      * or <code>~/</code>.
535      * <p>
536      * This method will handle a file in either Unix or Windows format.
537      * The prefix includes the first slash in the full filename where applicable.
538      * <pre>
539      * Windows:
540      * a\b\c.txt --> "" --> relative
541      * \a\b\c.txt --> "\" --> current drive absolute
542      * C:a\b\c.txt --> "C:" --> drive relative
543      * C:\a\b\c.txt --> "C:\" --> absolute
544      * \\server\a\b\c.txt --> "\\server\" --> UNC
545      *
546      * Unix:
547      * a/b/c.txt --> "" --> relative
548      * /a/b/c.txt --> "/" --> absolute
549      * ~/a/b/c.txt --> "~/" --> current user
550      * ~ --> "~/" --> current user (slash added)
551      * ~user/a/b/c.txt --> "~user/" --> named user
552      * ~user --> "~user/" --> named user (slash added)
553      * </pre>
554      * <p>
555      * The output will be the same irrespective of the machine that the code is running on.
556      * ie. both Unix and Windows prefixes are matched regardless.
557      *
558      * @param filename the filename to query, null returns null
559      * @return the prefix of the file, null if invalid
560      */

561     public static String JavaDoc getPrefix(String JavaDoc filename) {
562         if (filename == null) {
563             return null;
564         }
565         int len = getPrefixLength(filename);
566         if (len < 0) {
567             return null;
568         }
569         if (len > filename.length()) {
570             return filename + UNIX_SEPARATOR; // we know this only happens for unix
571
}
572         return filename.substring(0, len);
573     }
574
575     /**
576      * Gets the path from a full filename, which excludes the prefix.
577      * <p>
578      * This method will handle a file in either Unix or Windows format.
579      * The method is entirely text based, and returns the text before and
580      * including the last forward or backslash.
581      * <pre>
582      * C:\a\b\c.txt --> a\b\
583      * ~/a/b/c.txt --> a/b/
584      * a.txt --> ""
585      * a/b/c --> a/b/
586      * a/b/c/ --> a/b/c/
587      * </pre>
588      * <p>
589      * The output will be the same irrespective of the machine that the code is running on.
590      * <p>
591      * This method drops the prefix from the result.
592      * See {@link #getFullPath(String)} for the method that retains the prefix.
593      *
594      * @param filename the filename to query, null returns null
595      * @return the path of the file, an empty string if none exists, null if invalid
596      */

597     public static String JavaDoc getPath(String JavaDoc filename) {
598         return doGetPath(filename, 1);
599     }
600
601     /**
602      * Gets the path from a full filename, which excludes the prefix, and
603      * also excluding the final directory separator.
604      * <p>
605      * This method will handle a file in either Unix or Windows format.
606      * The method is entirely text based, and returns the text before the
607      * last forward or backslash.
608      * <pre>
609      * C:\a\b\c.txt --> a\b
610      * ~/a/b/c.txt --> a/b
611      * a.txt --> ""
612      * a/b/c --> a/b
613      * a/b/c/ --> a/b/c
614      * </pre>
615      * <p>
616      * The output will be the same irrespective of the machine that the code is running on.
617      * <p>
618      * This method drops the prefix from the result.
619      * See {@link #getFullPathNoEndSeparator(String)} for the method that retains the prefix.
620      *
621      * @param filename the filename to query, null returns null
622      * @return the path of the file, an empty string if none exists, null if invalid
623      */

624     public static String JavaDoc getPathNoEndSeparator(String JavaDoc filename) {
625         return doGetPath(filename, 0);
626     }
627
628     /**
629      * Does the work of getting the path.
630      *
631      * @param filename the filename
632      * @param separatorAdd 0 to omit the end separator, 1 to return it
633      * @return the path
634      */

635     private static String JavaDoc doGetPath(String JavaDoc filename, int separatorAdd) {
636         if (filename == null) {
637             return null;
638         }
639         int prefix = getPrefixLength(filename);
640         if (prefix < 0) {
641             return null;
642         }
643         int index = indexOfLastSeparator(filename);
644         if (prefix >= filename.length() || index < 0) {
645             return "";
646         }
647         return filename.substring(prefix, index + separatorAdd);
648     }
649
650     /**
651      * Gets the full path from a full filename, which is the prefix + path.
652      * <p>
653      * This method will handle a file in either Unix or Windows format.
654      * The method is entirely text based, and returns the text before and
655      * including the last forward or backslash.
656      * <pre>
657      * C:\a\b\c.txt --> C:\a\b\
658      * ~/a/b/c.txt --> ~/a/b/
659      * a.txt --> ""
660      * a/b/c --> a/b/
661      * a/b/c/ --> a/b/c/
662      * C: --> C:
663      * C:\ --> C:\
664      * ~ --> ~/
665      * ~/ --> ~/
666      * ~user --> ~user/
667      * ~user/ --> ~user/
668      * </pre>
669      * <p>
670      * The output will be the same irrespective of the machine that the code is running on.
671      *
672      * @param filename the filename to query, null returns null
673      * @return the path of the file, an empty string if none exists, null if invalid
674      */

675     public static String JavaDoc getFullPath(String JavaDoc filename) {
676         return doGetFullPath(filename, true);
677     }
678
679     /**
680      * Gets the full path from a full filename, which is the prefix + path,
681      * and also excluding the final directory separator.
682      * <p>
683      * This method will handle a file in either Unix or Windows format.
684      * The method is entirely text based, and returns the text before the
685      * last forward or backslash.
686      * <pre>
687      * C:\a\b\c.txt --> C:\a\b
688      * ~/a/b/c.txt --> ~/a/b
689      * a.txt --> ""
690      * a/b/c --> a/b
691      * a/b/c/ --> a/b/c
692      * C: --> C:
693      * C:\ --> C:\
694      * ~ --> ~
695      * ~/ --> ~
696      * ~user --> ~user
697      * ~user/ --> ~user
698      * </pre>
699      * <p>
700      * The output will be the same irrespective of the machine that the code is running on.
701      *
702      * @param filename the filename to query, null returns null
703      * @return the path of the file, an empty string if none exists, null if invalid
704      */

705     public static String JavaDoc getFullPathNoEndSeparator(String JavaDoc filename) {
706         return doGetFullPath(filename, false);
707     }
708
709     /**
710      * Does the work of getting the path.
711      *
712      * @param filename the filename
713      * @param includeSeparator true to include the end separator
714      * @return the path
715      */

716     private static String JavaDoc doGetFullPath(String JavaDoc filename, boolean includeSeparator) {
717         if (filename == null) {
718             return null;
719         }
720         int prefix = getPrefixLength(filename);
721         if (prefix < 0) {
722             return null;
723         }
724         if (prefix >= filename.length()) {
725             if (includeSeparator) {
726                 return getPrefix(filename); // add end slash if necessary
727
} else {
728                 return filename;
729             }
730         }
731         int index = indexOfLastSeparator(filename);
732         if (index < 0) {
733             return filename.substring(0, prefix);
734         }
735         int end = index + (includeSeparator ? 1 : 0);
736         return filename.substring(0, end);
737     }
738
739     /**
740      * Gets the name minus the path from a full filename.
741      * <p>
742      * This method will handle a file in either Unix or Windows format.
743      * The text after the last forward or backslash is returned.
744      * <pre>
745      * a/b/c.txt --> c.txt
746      * a.txt --> a.txt
747      * a/b/c --> c
748      * a/b/c/ --> ""
749      * </pre>
750      * <p>
751      * The output will be the same irrespective of the machine that the code is running on.
752      *
753      * @param filename the filename to query, null returns null
754      * @return the name of the file without the path, or an empty string if none exists
755      */

756     public static String JavaDoc getName(String JavaDoc filename) {
757         if (filename == null) {
758             return null;
759         }
760         int index = indexOfLastSeparator(filename);
761         return filename.substring(index + 1);
762     }
763
764     /**
765      * Gets the base name, minus the full path and extension, from a full filename.
766      * <p>
767      * This method will handle a file in either Unix or Windows format.
768      * The text after the last forward or backslash and before the last dot is returned.
769      * <pre>
770      * a/b/c.txt --> c
771      * a.txt --> a
772      * a/b/c --> c
773      * a/b/c/ --> ""
774      * </pre>
775      * <p>
776      * The output will be the same irrespective of the machine that the code is running on.
777      *
778      * @param filename the filename to query, null returns null
779      * @return the name of the file without the path, or an empty string if none exists
780      */

781     public static String JavaDoc getBaseName(String JavaDoc filename) {
782         return removeExtension(getName(filename));
783     }
784
785     /**
786      * Gets the extension of a filename.
787      * <p>
788      * This method returns the textual part of the filename after the last dot.
789      * There must be no directory separator after the dot.
790      * <pre>
791      * foo.txt --> "txt"
792      * a/b/c.jpg --> "jpg"
793      * a/b.txt/c --> ""
794      * a/b/c --> ""
795      * </pre>
796      * <p>
797      * The output will be the same irrespective of the machine that the code is running on.
798      *
799      * @param filename the filename to retrieve the extension of.
800      * @return the extension of the file or an empty string if none exists.
801      */

802     public static String JavaDoc getExtension(String JavaDoc filename) {
803         if (filename == null) {
804             return null;
805         }
806         int index = indexOfExtension(filename);
807         if (index == -1) {
808             return "";
809         } else {
810             return filename.substring(index + 1);
811         }
812     }
813
814     //----------------------------------------------------------------------- remove
815

816     /**
817      * Removes the extension from a filename.
818      * <p>
819      * This method returns the textual part of the filename before the last dot.
820      * There must be no directory separator after the dot.
821      * <pre>
822      * foo.txt --> foo
823      * a\b\c.jpg --> a\b\c
824      * a\b\c --> a\b\c
825      * a.b\c --> a.b\c
826      * </pre>
827      * <p>
828      * The output will be the same irrespective of the machine that the code is running on.
829      *
830      * @param filename the filename to query, null returns null
831      * @return the filename minus the extension
832      */

833     public static String JavaDoc removeExtension(String JavaDoc filename) {
834         if (filename == null) {
835             return null;
836         }
837         int index = indexOfExtension(filename);
838         if (index == -1) {
839             return filename;
840         } else {
841             return filename.substring(0, index);
842         }
843     }
844
845     // ---------------------------------------------------------------- equals
846

847     /**
848      * Checks whether two filenames are equal exactly.
849      */

850     public static boolean equals(String JavaDoc filename1, String JavaDoc filename2) {
851         return equals(filename1, filename2, false);
852     }
853
854     /**
855      * Checks whether two filenames are equal using the case rules of the system.
856      */

857     public static boolean equalsOnSystem(String JavaDoc filename1, String JavaDoc filename2) {
858         return equals(filename1, filename2, true);
859     }
860
861     /**
862      * Checks whether two filenames are equal optionally using the case rules of the system.
863      * <p>
864      *
865      * @param filename1 the first filename to query, may be null
866      * @param filename2 the second filename to query, may be null
867      * @param system whether to use the system (windows or unix)
868      * @return true if the filenames are equal, null equals null
869      */

870     private static boolean equals(String JavaDoc filename1, String JavaDoc filename2, boolean system) {
871         //noinspection StringEquality
872
if (filename1 == filename2) {
873             return true;
874         }
875         if (filename1 == null || filename2 == null) {
876             return false;
877         }
878         if (system && (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR)) {
879             return filename1.equalsIgnoreCase(filename2);
880         } else {
881             return filename1.equals(filename2);
882         }
883     }
884
885     // ---------------------------------------------------------------- split
886

887     /**
888      * Splits filename into a array of four Strings containing prefix, path, basename and extension.
889      * Path will contain ending separator.
890      */

891     public static String JavaDoc[] split(String JavaDoc filename) {
892         String JavaDoc prefix = getPrefix(filename);
893         if (prefix == null) {
894             prefix = "";
895         }
896         int lastSeparatorIndex = indexOfLastSeparator(filename);
897         int lastExtensionIndex = indexOfExtension(filename);
898
899         String JavaDoc path;
900         String JavaDoc baseName;
901         String JavaDoc extension;
902
903         if (lastSeparatorIndex == -1) {
904             path = "";
905             if (lastExtensionIndex == -1) {
906                 baseName = filename.substring(prefix.length());
907                 extension = "";
908             } else {
909                 baseName = filename.substring(prefix.length(), lastExtensionIndex);
910                 extension = filename.substring(lastExtensionIndex + 1);
911             }
912         } else {
913             path = filename.substring(prefix.length(), lastSeparatorIndex + 1);
914             if (lastExtensionIndex == -1) {
915                 baseName = filename.substring(prefix.length() + path.length());
916                 extension = "";
917             } else {
918                 baseName = filename.substring(prefix.length() + path.length(), lastExtensionIndex);
919                 extension = filename.substring(lastExtensionIndex + 1);
920             }
921         }
922         return new String JavaDoc[] {prefix, path, baseName, extension};
923     }
924
925 }
Popular Tags