KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > schlichtherle > io > util > Paths


1 /*
2  * Copyright 2006 Schlichtherle IT Services
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package de.schlichtherle.io.util;
18
19 import java.io.File JavaDoc;
20
21 /**
22  * Utility methods for file path names.
23  *
24  * @author Christian Schlichtherle
25  * @version @version@
26  */

27 public class Paths {
28
29     /**
30      * Equivalent to {@link #normalize(String, char)
31      * normalize(path, File.separatorChar)}.
32      */

33     public static final String JavaDoc normalize(
34             final String JavaDoc path) {
35         return normalize(path, File.separatorChar);
36     }
37
38     /**
39      * Removes all redundant separators, dot directories (<code>"."</code>)
40      * and dot-dot directories (<code>".."</code>) from the path and returns
41      * the result.
42      * An empty path results in <code>"."</code>.
43      * On Windows, a path may be prefixed by a drive letter followed by a
44      * colon.
45      * On all platforms, a path may be prefixed by two leading separators
46      * to indicate a UNC, although this is currently supported on Windows
47      * only.
48      * A single trailing separator character is always retained if present.
49      *
50      * @param path The path to normalize.
51      * @param separatorChar The path separator.
52      * @return <code>path</code> if it was already in normalized form.
53      * Otherwise, a new String with the normalized form of the given
54      * path.
55      * @throws NullPointerException If path is <code>null</code>.
56      */

57     public static String JavaDoc normalize(
58             final String JavaDoc path,
59             final char separatorChar) {
60         final int prefixLen = prefixLength(path, separatorChar);
61         final int pathLen = path.length();
62         final StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(pathLen);
63         normalize(path.substring(prefixLen, pathLen), separatorChar, 0, pathLen - prefixLen, buffer);
64         buffer.insert(0, path.substring(0, prefixLen));
65         if (buffer.length() == prefixLen
66                 && (prefixLen <= 0 || buffer.charAt(prefixLen - 1) != separatorChar))
67             buffer.append('.');
68         if (pathLen > 0 && path.charAt(pathLen - 1) == separatorChar)
69             if (buffer.charAt(buffer.length() - 1) != separatorChar)
70                 buffer.append(separatorChar); // retain trailing separator
71
final String JavaDoc result = buffer.toString();
72         return path.equals(result) ? path : result;
73     }
74
75     /**
76      * Removes all redundant separators, dot directories (<code>"."</code>)
77      * and dot-dot directories (<code>".."</code>) from the path and collects
78      * the result in a string buffer.
79      * This is a recursive call: The top level call should provide
80      * <code>0</code> as the <code>skip</code> parameter, the length
81      * of the path as the <code>end</code> parameter and an empty string
82      * buffer as the <code>result</code> parameter.
83      *
84      * @param path The path to normalize. A leading separator is ignored.
85      * <code>null</code> is not allowed.
86      * @param separatorChar The path separator.
87      * @param skip The number of elements in the path to skip because they are
88      * followed by a dot-dot directory.
89      * Must not be negative.
90      * @param end Only the string to the left of this index in <code>path</code>
91      * is considered.
92      * If not positive, nothing happens.
93      * @param result The string buffer with the collected results.
94      * <code>null</code> is not allowed.
95      * @return The number of elements in the path actually skipped.
96      * This is always lesser than or equal to the value of the
97      * parameter <code>skip</code>.
98      */

99     private static int normalize(
100             final String JavaDoc path,
101             final char separatorChar,
102             final int skip,
103             final int end,
104             final StringBuffer JavaDoc result) {
105         assert skip >= 0;
106         if (end <= 0)
107             return 0;
108
109         final int next = path.lastIndexOf(separatorChar, end - 1);
110         final String JavaDoc base = path.substring(next + 1, end);
111         final int skipped;
112         if (base.length() == 0 || ".".equals(base)) {
113             return normalize(path, separatorChar, skip, next, result);
114         } else if ("..".equals(base)) {
115             final int toSkip = skip + 1;
116             skipped = normalize(path, separatorChar, toSkip, next, result);
117             assert skipped <= toSkip;
118             if (skipped == toSkip)
119                 return skip;
120         } else if (skip > 0) {
121             return normalize(path, separatorChar, skip - 1, next, result) + 1;
122         } else {
123             assert skip == 0;
124             skipped = normalize(path, separatorChar, skip, next, result);
125             assert skipped == 0;
126         }
127
128         final int resultLen = result.length();
129         if (resultLen > 0 && result.charAt(resultLen - 1) != separatorChar)
130             result.append(separatorChar);
131         result.append(base);
132         return skipped;
133     }
134
135     /**
136      * Cuts off any separator characters at the end of the path, unless the
137      * path contains of only separator characters, in which case a single
138      * separator character is retained to denote the root directory.
139      *
140      * @return <code>path</code> if it's a path without trailing separators
141      * or contains the separator only.
142      * Otherwise, the substring until the first of at least one
143      * separating characters is returned.
144      * @throws NullPointerException If path is <code>null</code>.
145      */

146     public final static String JavaDoc cutTrailingSeparators(
147             final String JavaDoc path,
148             final char separatorChar) {
149         int i = path.length();
150         if (i <= 0 || path.charAt(--i) != separatorChar)
151             return path;
152         while (i > 0 && path.charAt(--i) == separatorChar)
153             ;
154         return path.substring(0, ++i);
155     }
156
157     /**
158      * Cuts off a trailing separator character of the pathname, unless the
159      * pathname contains of only the separator character (i.e. denotes the
160      * root directory).
161      *
162      * @deprecated This method chops off a single trailing separator only.
163      * Use {@link #cutTrailingSeparators} to chop off multiple
164      * trailing separators.
165      * @return <code>path</code> if it's a path without a trailing separator
166      * or contains the separator only.
167      * Otherwise, the substring up to the last character is returned.
168      * @throws NullPointerException If path is <code>null</code>.
169      */

170     public final static String JavaDoc cutTrailingSeparator(
171             final String JavaDoc path,
172             final char separatorChar) {
173         final int pathEnd = path.length() - 1;
174         if (pathEnd > 0 && path.charAt(pathEnd) == separatorChar)
175             return path.substring(0, pathEnd);
176         else
177             return path;
178     }
179
180     /**
181      * Equivalent to {@link #split(String, char)
182      * split(path, File.separatorChar)}.
183      */

184     public static final String JavaDoc[] split(
185             final String JavaDoc path) {
186         return split(path, File.separatorChar);
187     }
188
189     /**
190      * Splits a path into its parent path and its base name,
191      * recognizing platform specific file system roots.
192      *
193      *
194      * @param path The name of the path which's parent path and base name
195      * are to be returned.
196      * @param separatorChar The path separator to use for this operation.
197      * @return An array of at least two strings:
198      * <ol>
199      * <li>Index 0 holds the parent path or <code>null</code> if the
200      * path does not specify a parent. This name compares equal
201      * with {@link java.io.File#getParent()}, except that
202      * redundant separators left of the parent path's base name
203      * are kept (base.e. empty path elements between two separators
204      * left of the parent path's base name are not removed).</li>
205      * <li>Index 1 holds the base name. This name compares
206      * equal with {@link java.io.File#getName()}.</li>
207      * </ol>
208      * @throws NullPointerException If path is <code>null</code>.
209      */

210     public static final String JavaDoc[] split(
211             final String JavaDoc path,
212             final char separatorChar) {
213         return split(path, separatorChar, new String JavaDoc[2]);
214     }
215
216     /**
217      * Same as {@link #split(String, char)}, but uses the given array
218      * <code>split</code> to store the result.
219      *
220      * @param split An array of at least two String elements to hold the
221      * result.
222      * @return <code>split</code>
223      */

224     public static String JavaDoc[] split(
225             final String JavaDoc path,
226             final char separatorChar,
227             final String JavaDoc[] split) {
228         final int prefix = prefixLength(path, separatorChar);
229
230         // Now skip any trailing separators and look for the previous separator.
231
int base = -1;
232         int end = path.length() - 1;
233         if (prefix <= end) {
234             end = lastIndexNot(path, separatorChar, end);
235             base = path.lastIndexOf(separatorChar, end);
236             if (base < prefix)
237                 base = -1;
238         }
239         end++; // convert end index to interval boundary
240

241         // Finally split according to our findings.
242
if (base != -1) { // found separator after the prefix?
243
final int j = lastIndexNot(path, separatorChar, base) + 1;
244             split[0] = path.substring(0, j > prefix ? j : prefix);
245             split[1] = path.substring(base + 1, end);
246         } else { // no separator
247
if (0 < prefix && prefix < end) // prefix exists and we have more?
248
split[0] = path.substring(0, prefix); // prefix is parent
249
else
250                 split[0] = null; // no parent
251
split[1] = path.substring(prefix, end);
252         }
253
254         return split;
255     }
256
257     /**
258      * Returns the length of the file system prefix in <code>path</code>.
259      * File system prefixes are:
260      * <ol>
261      * <li>Two leading separators.
262      * On Windows, this is the notation for a UNC.
263      * <li>A letter followed by a colon and optionally a separator.
264      * On Windows, this is the notation for a drive.
265      * </ol>
266      * This method works the same on all platforms, so even if the separator
267      * is <code>'/'</code>, two leading separators would be considered to
268      * be a UNC and hence the return value would be <code>2</code>.
269      *
270      * @param path The file system path.
271      * @param separatorChar The separator character.
272      * @return The number of characters in the prefix.
273      * @throws NullPointerException If <code>path</code> is <code>null</code>.
274      */

275     private static int prefixLength(final String JavaDoc path, final char separatorChar) {
276         final int pathLen = path.length();
277         int len = 0; // default prefix length
278
if (pathLen > 0 && path.charAt(0) == separatorChar) {
279             len++; // leading separator or first character of a UNC.
280
} else if (pathLen > 1 && path.charAt(1) == ':') {
281             final char drive = path.charAt(0);
282             if ('A' <= drive && drive <= 'Z'
283                     || 'a' <= drive && drive <= 'z') { // US-ASCII letters only
284
// Path is prefixed with drive, e.g. "C:\\Programs".
285
len = 2;
286             }
287         }
288         if (pathLen > len && path.charAt(len) == separatorChar)
289             len++; // leading separator is considered part of prefix
290
return len;
291     }
292
293     private static final int lastIndexNot(String JavaDoc path, char separatorChar, int last) {
294         while (path.charAt(last) == separatorChar && --last >= 0)
295             ;
296         return last;
297     }
298     
299     /** You cannot instantiate this class. */
300     protected Paths() {
301     }
302 }
303
Popular Tags