KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > runtime > Path


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.runtime;
12
13 import java.io.File JavaDoc;
14
15 /**
16  * The standard implementation of the <code>IPath</code> interface.
17  * Paths are always maintained in canonicalized form. That is, parent
18  * references (i.e., <code>../../</code>) and duplicate separators are
19  * resolved. For example,
20  * <pre> new Path("/a/b").append("../foo/bar")</pre>
21  * will yield the path
22  * <pre> /a/foo/bar</pre>
23  * <p>
24  * This class can be used without OSGi running.
25  * </p><p>
26  * This class is not intended to be subclassed by clients but
27  * may be instantiated.
28  * </p>
29  * @see IPath
30  */

31 public class Path implements IPath, Cloneable JavaDoc {
32     /** masks for separator values */
33     private static final int HAS_LEADING = 1;
34     private static final int IS_UNC = 2;
35     private static final int HAS_TRAILING = 4;
36
37     private static final int ALL_SEPARATORS = HAS_LEADING | IS_UNC | HAS_TRAILING;
38
39     /** Constant empty string value. */
40     private static final String JavaDoc EMPTY_STRING = ""; //$NON-NLS-1$
41

42     /** Constant value indicating no segments */
43     private static final String JavaDoc[] NO_SEGMENTS = new String JavaDoc[0];
44
45     /** Constant value containing the empty path with no device. */
46     public static final Path EMPTY = new Path(EMPTY_STRING);
47
48     /** Mask for all bits that are involved in the hash code */
49     private static final int HASH_MASK = ~HAS_TRAILING;
50
51
52     /** Constant root path string (<code>"/"</code>). */
53     private static final String JavaDoc ROOT_STRING = "/"; //$NON-NLS-1$
54

55     /** Constant value containing the root path with no device. */
56     public static final Path ROOT = new Path(ROOT_STRING);
57     
58     /** Constant value indicating if the current platform is Windows */
59     private static final boolean WINDOWS = java.io.File.separatorChar == '\\';
60
61     /** The device id string. May be null if there is no device. */
62     private String JavaDoc device = null;
63
64     //Private implementation note: the segments and separators
65
//arrays are never modified, so that they can be shared between
66
//path instances
67

68     /** The path segments */
69     private String JavaDoc[] segments;
70
71     /** flags indicating separators (has leading, is UNC, has trailing) */
72     private int separators;
73
74     /**
75      * Constructs a new path from the given string path.
76      * The string path must represent a valid file system path
77      * on the local file system.
78      * The path is canonicalized and double slashes are removed
79      * except at the beginning. (to handle UNC paths). All forward
80      * slashes ('/') are treated as segment delimiters, and any
81      * segment and device delimiters for the local file system are
82      * also respected.
83      *
84      * @param pathString the portable string path
85      * @see IPath#toPortableString()
86      * @since 3.1
87      */

88     public static IPath fromOSString(String JavaDoc pathString) {
89         return new Path(pathString);
90     }
91     
92     /**
93      * Constructs a new path from the given path string.
94      * The path string must have been produced by a previous
95      * call to <code>IPath.toPortableString</code>.
96      *
97      * @param pathString the portable path string
98      * @see IPath#toPortableString()
99      * @since 3.1
100      */

101     public static IPath fromPortableString(String JavaDoc pathString) {
102         int firstMatch = pathString.indexOf(DEVICE_SEPARATOR) +1;
103         //no extra work required if no device characters
104
if (firstMatch <= 0)
105             return new Path().initialize(null, pathString);
106         //if we find a single colon, then the path has a device
107
String JavaDoc devicePart = null;
108         int pathLength = pathString.length();
109         if (firstMatch == pathLength || pathString.charAt(firstMatch) != DEVICE_SEPARATOR) {
110             devicePart = pathString.substring(0, firstMatch);
111             pathString = pathString.substring(firstMatch, pathLength);
112         }
113         //optimize for no colon literals
114
if (pathString.indexOf(DEVICE_SEPARATOR) == -1)
115             return new Path().initialize(devicePart, pathString);
116         //contract colon literals
117
char[] chars = pathString.toCharArray();
118         int readOffset = 0, writeOffset = 0, length = chars.length;
119         while (readOffset < length) {
120             if (chars[readOffset] == DEVICE_SEPARATOR)
121                 if (++readOffset >= length)
122                     break;
123             chars[writeOffset++] = chars[readOffset++];
124         }
125         return new Path().initialize(devicePart, new String JavaDoc(chars, 0, writeOffset));
126     }
127     
128     /* (Intentionally not included in javadoc)
129      * Private constructor.
130      */

131     private Path() {
132         // not allowed
133
}
134
135     /**
136      * Constructs a new path from the given string path.
137      * The string path must represent a valid file system path
138      * on the local file system.
139      * The path is canonicalized and double slashes are removed
140      * except at the beginning. (to handle UNC paths). All forward
141      * slashes ('/') are treated as segment delimiters, and any
142      * segment and device delimiters for the local file system are
143      * also respected (such as colon (':') and backslash ('\') on some file systems).
144      *
145      * @param fullPath the string path
146      * @see #isValidPath(String)
147      */

148     public Path(String JavaDoc fullPath) {
149         String JavaDoc devicePart = null;
150         if (WINDOWS) {
151             //convert backslash to forward slash
152
fullPath = fullPath.indexOf('\\') == -1 ? fullPath : fullPath.replace('\\', SEPARATOR);
153             //extract device
154
int i = fullPath.indexOf(DEVICE_SEPARATOR);
155             if (i != -1) {
156                 //remove leading slash from device part to handle output of URL.getFile()
157
int start = fullPath.charAt(0) == SEPARATOR ? 1 : 0;
158                 devicePart = fullPath.substring(start, i + 1);
159                 fullPath = fullPath.substring(i + 1, fullPath.length());
160             }
161         }
162         initialize(devicePart, fullPath);
163     }
164
165     /**
166      * Constructs a new path from the given device id and string path.
167      * The given string path must be valid.
168      * The path is canonicalized and double slashes are removed except
169      * at the beginning (to handle UNC paths). All forward
170      * slashes ('/') are treated as segment delimiters, and any
171      * segment delimiters for the local file system are
172      * also respected (such as backslash ('\') on some file systems).
173      *
174      * @param device the device id
175      * @param path the string path
176      * @see #isValidPath(String)
177      * @see #setDevice(String)
178      */

179     public Path(String JavaDoc device, String JavaDoc path) {
180         if (WINDOWS) {
181             //convert backslash to forward slash
182
path = path.indexOf('\\') == -1 ? path : path.replace('\\', SEPARATOR);
183         }
184         initialize(device, path);
185     }
186
187     /* (Intentionally not included in javadoc)
188      * Private constructor.
189      */

190     private Path(String JavaDoc device, String JavaDoc[] segments, int _separators) {
191         // no segment validations are done for performance reasons
192
this.segments = segments;
193         this.device = device;
194         //hash code is cached in all but the bottom three bits of the separators field
195
this.separators = (computeHashCode() << 3) | (_separators & ALL_SEPARATORS);
196     }
197
198     /* (Intentionally not included in javadoc)
199      * @see IPath#addFileExtension
200      */

201     public IPath addFileExtension(String JavaDoc extension) {
202         if (isRoot() || isEmpty() || hasTrailingSeparator())
203             return this;
204         int len = segments.length;
205         String JavaDoc[] newSegments = new String JavaDoc[len];
206         System.arraycopy(segments, 0, newSegments, 0, len - 1);
207         newSegments[len - 1] = segments[len - 1] + '.' + extension;
208         return new Path(device, newSegments, separators);
209     }
210
211     /* (Intentionally not included in javadoc)
212      * @see IPath#addTrailingSeparator
213      */

214     public IPath addTrailingSeparator() {
215         if (hasTrailingSeparator() || isRoot()) {
216             return this;
217         }
218         //XXX workaround, see 1GIGQ9V
219
if (isEmpty()) {
220             return new Path(device, segments, HAS_LEADING);
221         }
222         return new Path(device, segments, separators | HAS_TRAILING);
223     }
224
225     /* (Intentionally not included in javadoc)
226      * @see IPath#append(IPath)
227      */

228     public IPath append(IPath tail) {
229         //optimize some easy cases
230
if (tail == null || tail.segmentCount() == 0)
231             return this;
232         //these call chains look expensive, but in most cases they are no-ops
233
if (this.isEmpty())
234             return tail.setDevice(device).makeRelative().makeUNC(isUNC());
235         if (this.isRoot())
236             return tail.setDevice(device).makeAbsolute().makeUNC(isUNC());
237
238         //concatenate the two segment arrays
239
int myLen = segments.length;
240         int tailLen = tail.segmentCount();
241         String JavaDoc[] newSegments = new String JavaDoc[myLen + tailLen];
242         System.arraycopy(segments, 0, newSegments, 0, myLen);
243         for (int i = 0; i < tailLen; i++) {
244             newSegments[myLen + i] = tail.segment(i);
245         }
246         //use my leading separators and the tail's trailing separator
247
Path result = new Path(device, newSegments, (separators & (HAS_LEADING | IS_UNC)) | (tail.hasTrailingSeparator() ? HAS_TRAILING : 0));
248         String JavaDoc tailFirstSegment = newSegments[myLen];
249         if (tailFirstSegment.equals("..") || tailFirstSegment.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$
250
result.canonicalize();
251         }
252         return result;
253     }
254
255     /* (Intentionally not included in javadoc)
256      * @see IPath#append(java.lang.String)
257      */

258     public IPath append(String JavaDoc tail) {
259         //optimize addition of a single segment
260
if (tail.indexOf(SEPARATOR) == -1 && tail.indexOf("\\") == -1 && tail.indexOf(DEVICE_SEPARATOR) == -1) { //$NON-NLS-1$
261
int tailLength = tail.length();
262             if (tailLength < 3) {
263                 //some special cases
264
if (tailLength == 0 || ".".equals(tail)) { //$NON-NLS-1$
265
return this;
266                 }
267                 if ("..".equals(tail)) //$NON-NLS-1$
268
return removeLastSegments(1);
269             }
270             //just add the segment
271
int myLen = segments.length;
272             String JavaDoc[] newSegments = new String JavaDoc[myLen + 1];
273             System.arraycopy(segments, 0, newSegments, 0, myLen);
274             newSegments[myLen] = tail;
275             return new Path(device, newSegments, separators & ~HAS_TRAILING);
276         }
277         //go with easy implementation
278
return append(new Path(tail));
279     }
280
281     /**
282      * Destructively converts this path to its canonical form.
283      * <p>
284      * In its canonical form, a path does not have any
285      * "." segments, and parent references ("..") are collapsed
286      * where possible.
287      * </p>
288      * @return true if the path was modified, and false otherwise.
289      */

290     private boolean canonicalize() {
291         //look for segments that need canonicalizing
292
for (int i = 0, max = segments.length; i < max; i++) {
293             String JavaDoc segment = segments[i];
294             if (segment.charAt(0) == '.' && (segment.equals("..") || segment.equals("."))) { //$NON-NLS-1$ //$NON-NLS-2$
295
//path needs to be canonicalized
296
collapseParentReferences();
297                 //paths of length 0 have no trailing separator
298
if (segments.length == 0)
299                     separators &= (HAS_LEADING | IS_UNC);
300                 //recompute hash because canonicalize affects hash
301
separators = (separators & ALL_SEPARATORS) | (computeHashCode() << 3);
302                 return true;
303             }
304         }
305         return false;
306     }
307
308     /* (Intentionally not included in javadoc)
309      * Clones this object.
310      */

311     public Object JavaDoc clone() {
312         try {
313             return super.clone();
314         } catch (CloneNotSupportedException JavaDoc e) {
315             return null;
316         }
317     }
318
319     /**
320      * Destructively removes all occurrences of ".." segments from this path.
321      */

322     private void collapseParentReferences() {
323         int segmentCount = segments.length;
324         String JavaDoc[] stack = new String JavaDoc[segmentCount];
325         int stackPointer = 0;
326         for (int i = 0; i < segmentCount; i++) {
327             String JavaDoc segment = segments[i];
328             if (segment.equals("..")) { //$NON-NLS-1$
329
if (stackPointer == 0) {
330                     // if the stack is empty we are going out of our scope
331
// so we need to accumulate segments. But only if the original
332
// path is relative. If it is absolute then we can't go any higher than
333
// root so simply toss the .. references.
334
if (!isAbsolute())
335                         stack[stackPointer++] = segment; //stack push
336
} else {
337                     // if the top is '..' then we are accumulating segments so don't pop
338
if ("..".equals(stack[stackPointer - 1])) //$NON-NLS-1$
339
stack[stackPointer++] = ".."; //$NON-NLS-1$
340
else
341                         stackPointer--;
342                     //stack pop
343
}
344                 //collapse current references
345
} else if (!segment.equals(".") || segmentCount == 1) //$NON-NLS-1$
346
stack[stackPointer++] = segment; //stack push
347
}
348         //if the number of segments hasn't changed, then no modification needed
349
if (stackPointer == segmentCount)
350             return;
351         //build the new segment array backwards by popping the stack
352
String JavaDoc[] newSegments = new String JavaDoc[stackPointer];
353         System.arraycopy(stack, 0, newSegments, 0, stackPointer);
354         this.segments = newSegments;
355     }
356
357     /**
358      * Removes duplicate slashes from the given path, with the exception
359      * of leading double slash which represents a UNC path.
360      */

361     private String JavaDoc collapseSlashes(String JavaDoc path) {
362         int length = path.length();
363         // if the path is only 0, 1 or 2 chars long then it could not possibly have illegal
364
// duplicate slashes.
365
if (length < 3)
366             return path;
367         // check for an occurrence of // in the path. Start at index 1 to ensure we skip leading UNC //
368
// If there are no // then there is nothing to collapse so just return.
369
if (path.indexOf("//", 1) == -1) //$NON-NLS-1$
370
return path;
371         // We found an occurrence of // in the path so do the slow collapse.
372
char[] result = new char[path.length()];
373         int count = 0;
374         boolean hasPrevious = false;
375         char[] characters = path.toCharArray();
376         for (int index = 0; index < characters.length; index++) {
377             char c = characters[index];
378             if (c == SEPARATOR) {
379                 if (hasPrevious) {
380                     // skip double slashes, except for beginning of UNC.
381
// note that a UNC path can't have a device.
382
if (device == null && index == 1) {
383                         result[count] = c;
384                         count++;
385                     }
386                 } else {
387                     hasPrevious = true;
388                     result[count] = c;
389                     count++;
390                 }
391             } else {
392                 hasPrevious = false;
393                 result[count] = c;
394                 count++;
395             }
396         }
397         return new String JavaDoc(result, 0, count);
398     }
399
400     /* (Intentionally not included in javadoc)
401      * Computes the hash code for this object.
402      */

403     private int computeHashCode() {
404         int hash = device == null ? 17 : device.hashCode();
405         int segmentCount = segments.length;
406         for (int i = 0; i < segmentCount; i++) {
407             //this function tends to given a fairly even distribution
408
hash = hash * 37 + segments[i].hashCode();
409         }
410         return hash;
411     }
412
413     /* (Intentionally not included in javadoc)
414      * Returns the size of the string that will be created by toString or toOSString.
415      */

416     private int computeLength() {
417         int length = 0;
418         if (device != null)
419             length += device.length();
420         if ((separators & HAS_LEADING) != 0)
421             length++;
422         if ((separators & IS_UNC) != 0)
423             length++;
424         //add the segment lengths
425
int max = segments.length;
426         if (max > 0) {
427             for (int i = 0; i < max; i++) {
428                 length += segments[i].length();
429             }
430             //add the separator lengths
431
length += max - 1;
432         }
433         if ((separators & HAS_TRAILING) != 0)
434             length++;
435         return length;
436     }
437
438     /* (Intentionally not included in javadoc)
439      * Returns the number of segments in the given path
440      */

441     private int computeSegmentCount(String JavaDoc path) {
442         int len = path.length();
443         if (len == 0 || (len == 1 && path.charAt(0) == SEPARATOR)) {
444             return 0;
445         }
446         int count = 1;
447         int prev = -1;
448         int i;
449         while ((i = path.indexOf(SEPARATOR, prev + 1)) != -1) {
450             if (i != prev + 1 && i != len) {
451                 ++count;
452             }
453             prev = i;
454         }
455         if (path.charAt(len - 1) == SEPARATOR) {
456             --count;
457         }
458         return count;
459     }
460
461     /**
462      * Computes the segment array for the given canonicalized path.
463      */

464     private String JavaDoc[] computeSegments(String JavaDoc path) {
465         // performance sensitive --- avoid creating garbage
466
int segmentCount = computeSegmentCount(path);
467         if (segmentCount == 0)
468             return NO_SEGMENTS;
469         String JavaDoc[] newSegments = new String JavaDoc[segmentCount];
470         int len = path.length();
471         // check for initial slash
472
int firstPosition = (path.charAt(0) == SEPARATOR) ? 1 : 0;
473         // check for UNC
474
if (firstPosition == 1 && len > 1 && (path.charAt(1) == SEPARATOR))
475             firstPosition = 2;
476         int lastPosition = (path.charAt(len - 1) != SEPARATOR) ? len - 1 : len - 2;
477         // for non-empty paths, the number of segments is
478
// the number of slashes plus 1, ignoring any leading
479
// and trailing slashes
480
int next = firstPosition;
481         for (int i = 0; i < segmentCount; i++) {
482             int start = next;
483             int end = path.indexOf(SEPARATOR, next);
484             if (end == -1) {
485                 newSegments[i] = path.substring(start, lastPosition + 1);
486             } else {
487                 newSegments[i] = path.substring(start, end);
488             }
489             next = end + 1;
490         }
491         return newSegments;
492     }
493     /**
494      * Returns the platform-neutral encoding of the given segment onto
495      * the given string buffer. This escapes literal colon characters with double colons.
496      */

497     private void encodeSegment(String JavaDoc string, StringBuffer JavaDoc buf) {
498         int len = string.length();
499         for (int i = 0; i < len; i++) {
500             char c = string.charAt(i);
501             buf.append(c);
502             if (c == DEVICE_SEPARATOR)
503                 buf.append(DEVICE_SEPARATOR);
504         }
505     }
506
507     /* (Intentionally not included in javadoc)
508      * Compares objects for equality.
509      */

510     public boolean equals(Object JavaDoc obj) {
511         if (this == obj)
512             return true;
513         if (!(obj instanceof Path))
514             return false;
515         Path target = (Path) obj;
516         //check leading separators and hash code
517
if ((separators & HASH_MASK) != (target.separators & HASH_MASK))
518             return false;
519         String JavaDoc[] targetSegments = target.segments;
520         int i = segments.length;
521         //check segment count
522
if (i != targetSegments.length)
523             return false;
524         //check segments in reverse order - later segments more likely to differ
525
while (--i >= 0)
526             if (!segments[i].equals(targetSegments[i]))
527                 return false;
528         //check device last (least likely to differ)
529
return device == target.device || (device != null && device.equals(target.device));
530     }
531
532     /* (Intentionally not included in javadoc)
533      * @see IPath#getDevice
534      */

535     public String JavaDoc getDevice() {
536         return device;
537     }
538
539     /* (Intentionally not included in javadoc)
540      * @see IPath#getFileExtension
541      */

542     public String JavaDoc getFileExtension() {
543         if (hasTrailingSeparator()) {
544             return null;
545         }
546         String JavaDoc lastSegment = lastSegment();
547         if (lastSegment == null) {
548             return null;
549         }
550         int index = lastSegment.lastIndexOf('.');
551         if (index == -1) {
552             return null;
553         }
554         return lastSegment.substring(index + 1);
555     }
556
557     /* (Intentionally not included in javadoc)
558      * Computes the hash code for this object.
559      */

560     public int hashCode() {
561         return separators & HASH_MASK;
562     }
563
564     /* (Intentionally not included in javadoc)
565      * @see IPath#hasTrailingSeparator2
566      */

567     public boolean hasTrailingSeparator() {
568         return (separators & HAS_TRAILING) != 0;
569     }
570
571     /*
572      * Initialize the current path with the given string.
573      */

574     private IPath initialize(String JavaDoc deviceString, String JavaDoc path) {
575         Assert.isNotNull(path);
576         this.device = deviceString;
577
578         path = collapseSlashes(path);
579         int len = path.length();
580
581         //compute the separators array
582
if (len < 2) {
583             if (len == 1 && path.charAt(0) == SEPARATOR) {
584                 this.separators = HAS_LEADING;
585             } else {
586                 this.separators = 0;
587             }
588         } else {
589             boolean hasLeading = path.charAt(0) == SEPARATOR;
590             boolean isUNC = hasLeading && path.charAt(1) == SEPARATOR;
591             //UNC path of length two has no trailing separator
592
boolean hasTrailing = !(isUNC && len == 2) && path.charAt(len - 1) == SEPARATOR;
593             separators = hasLeading ? HAS_LEADING : 0;
594             if (isUNC)
595                 separators |= IS_UNC;
596             if (hasTrailing)
597                 separators |= HAS_TRAILING;
598         }
599         //compute segments and ensure canonical form
600
segments = computeSegments(path);
601         if (!canonicalize()) {
602             //compute hash now because canonicalize didn't need to do it
603
separators = (separators & ALL_SEPARATORS) | (computeHashCode() << 3);
604         }
605         return this;
606     }
607
608     /* (Intentionally not included in javadoc)
609      * @see IPath#isAbsolute
610      */

611     public boolean isAbsolute() {
612         //it's absolute if it has a leading separator
613
return (separators & HAS_LEADING) != 0;
614     }
615
616     /* (Intentionally not included in javadoc)
617      * @see IPath#isEmpty
618      */

619     public boolean isEmpty() {
620         //true if no segments and no leading prefix
621
return segments.length == 0 && ((separators & ALL_SEPARATORS) != HAS_LEADING);
622
623     }
624
625     /* (Intentionally not included in javadoc)
626      * @see IPath#isPrefixOf
627      */

628     public boolean isPrefixOf(IPath anotherPath) {
629         if (device == null) {
630             if (anotherPath.getDevice() != null) {
631                 return false;
632             }
633         } else {
634             if (!device.equalsIgnoreCase(anotherPath.getDevice())) {
635                 return false;
636             }
637         }
638         if (isEmpty() || (isRoot() && anotherPath.isAbsolute())) {
639             return true;
640         }
641         int len = segments.length;
642         if (len > anotherPath.segmentCount()) {
643             return false;
644         }
645         for (int i = 0; i < len; i++) {
646             if (!segments[i].equals(anotherPath.segment(i)))
647                 return false;
648         }
649         return true;
650     }
651
652     /* (Intentionally not included in javadoc)
653      * @see IPath#isRoot
654      */

655     public boolean isRoot() {
656         //must have no segments, a leading separator, and not be a UNC path.
657
return this == ROOT || (segments.length == 0 && ((separators & ALL_SEPARATORS) == HAS_LEADING));
658     }
659
660     /* (Intentionally not included in javadoc)
661      * @see IPath#isUNC
662      */

663     public boolean isUNC() {
664         if (device != null)
665             return false;
666         return (separators & IS_UNC) != 0;
667     }
668
669     /* (Intentionally not included in javadoc)
670      * @see IPath#isValidPath(String)
671      */

672     public boolean isValidPath(String JavaDoc path) {
673         Path test = new Path(path);
674         for (int i = 0, max = test.segmentCount(); i < max; i++)
675             if (!isValidSegment(test.segment(i)))
676                 return false;
677         return true;
678     }
679
680     /* (Intentionally not included in javadoc)
681      * @see IPath#isValidSegment(String)
682      */

683     public boolean isValidSegment(String JavaDoc segment) {
684         int size = segment.length();
685         if (size == 0)
686             return false;
687         for (int i = 0; i < size; i++) {
688             char c = segment.charAt(i);
689             if (c == '/')
690                 return false;
691             if (WINDOWS && (c == '\\' || c == ':'))
692                 return false;
693         }
694         return true;
695     }
696
697     /* (Intentionally not included in javadoc)
698      * @see IPath#lastSegment()
699      */

700     public String JavaDoc lastSegment() {
701         int len = segments.length;
702         return len == 0 ? null : segments[len - 1];
703     }
704
705     /* (Intentionally not included in javadoc)
706      * @see IPath#makeAbsolute()
707      */

708     public IPath makeAbsolute() {
709         if (isAbsolute()) {
710             return this;
711         }
712         Path result = new Path(device, segments, separators | HAS_LEADING);
713         //may need canonicalizing if it has leading ".." or "." segments
714
if (result.segmentCount() > 0) {
715             String JavaDoc first = result.segment(0);
716             if (first.equals("..") || first.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$
717
result.canonicalize();
718             }
719         }
720         return result;
721     }
722
723     /* (Intentionally not included in javadoc)
724      * @see IPath#makeRelative()
725      */

726     public IPath makeRelative() {
727         if (!isAbsolute()) {
728             return this;
729         }
730         return new Path(device, segments, separators & HAS_TRAILING);
731     }
732
733     /* (Intentionally not included in javadoc)
734      * @see IPath#makeUNC(boolean)
735      */

736     public IPath makeUNC(boolean toUNC) {
737         // if we are already in the right form then just return
738
if (!(toUNC ^ isUNC()))
739             return this;
740
741         int newSeparators = this.separators;
742         if (toUNC) {
743             newSeparators |= HAS_LEADING | IS_UNC;
744         } else {
745             //mask out the UNC bit
746
newSeparators &= HAS_LEADING | HAS_TRAILING;
747         }
748         return new Path(toUNC ? null : device, segments, newSeparators);
749     }
750
751     /* (Intentionally not included in javadoc)
752      * @see IPath#matchingFirstSegments(IPath)
753      */

754     public int matchingFirstSegments(IPath anotherPath) {
755         Assert.isNotNull(anotherPath);
756         int anotherPathLen = anotherPath.segmentCount();
757         int max = Math.min(segments.length, anotherPathLen);
758         int count = 0;
759         for (int i = 0; i < max; i++) {
760             if (!segments[i].equals(anotherPath.segment(i))) {
761                 return count;
762             }
763             count++;
764         }
765         return count;
766     }
767
768     /* (Intentionally not included in javadoc)
769      * @see IPath#removeFileExtension()
770      */

771     public IPath removeFileExtension() {
772         String JavaDoc extension = getFileExtension();
773         if (extension == null || extension.equals("")) { //$NON-NLS-1$
774
return this;
775         }
776         String JavaDoc lastSegment = lastSegment();
777         int index = lastSegment.lastIndexOf(extension) - 1;
778         return removeLastSegments(1).append(lastSegment.substring(0, index));
779     }
780
781     /* (Intentionally not included in javadoc)
782      * @see IPath#removeFirstSegments(int)
783      */

784     public IPath removeFirstSegments(int count) {
785         if (count == 0)
786             return this;
787         if (count >= segments.length) {
788             return new Path(device, NO_SEGMENTS, 0);
789         }
790         Assert.isLegal(count > 0);
791         int newSize = segments.length - count;
792         String JavaDoc[] newSegments = new String JavaDoc[newSize];
793         System.arraycopy(this.segments, count, newSegments, 0, newSize);
794
795         //result is always a relative path
796
return new Path(device, newSegments, separators & HAS_TRAILING);
797     }
798
799     /* (Intentionally not included in javadoc)
800      * @see IPath#removeLastSegments(int)
801      */

802     public IPath removeLastSegments(int count) {
803         if (count == 0)
804             return this;
805         if (count >= segments.length) {
806             //result will have no trailing separator
807
return new Path(device, NO_SEGMENTS, separators & (HAS_LEADING | IS_UNC));
808         }
809         Assert.isLegal(count > 0);
810         int newSize = segments.length - count;
811         String JavaDoc[] newSegments = new String JavaDoc[newSize];
812         System.arraycopy(this.segments, 0, newSegments, 0, newSize);
813         return new Path(device, newSegments, separators);
814     }
815
816     /* (Intentionally not included in javadoc)
817      * @see IPath#removeTrailingSeparator()
818      */

819     public IPath removeTrailingSeparator() {
820         if (!hasTrailingSeparator()) {
821             return this;
822         }
823         return new Path(device, segments, separators & (HAS_LEADING | IS_UNC));
824     }
825
826     /* (Intentionally not included in javadoc)
827      * @see IPath#segment(int)
828      */

829     public String JavaDoc segment(int index) {
830         if (index >= segments.length)
831             return null;
832         return segments[index];
833     }
834
835     /* (Intentionally not included in javadoc)
836      * @see IPath#segmentCount()
837      */

838     public int segmentCount() {
839         return segments.length;
840     }
841
842     /* (Intentionally not included in javadoc)
843      * @see IPath#segments()
844      */

845     public String JavaDoc[] segments() {
846         String JavaDoc[] segmentCopy = new String JavaDoc[segments.length];
847         System.arraycopy(segments, 0, segmentCopy, 0, segments.length);
848         return segmentCopy;
849     }
850
851     /* (Intentionally not included in javadoc)
852      * @see IPath#setDevice(String)
853      */

854     public IPath setDevice(String JavaDoc value) {
855         if (value != null) {
856             Assert.isTrue(value.indexOf(IPath.DEVICE_SEPARATOR) == (value.length() - 1), "Last character should be the device separator"); //$NON-NLS-1$
857
}
858         //return the receiver if the device is the same
859
if (value == device || (value != null && value.equals(device)))
860             return this;
861
862         return new Path(value, segments, separators);
863     }
864
865     /* (Intentionally not included in javadoc)
866      * @see IPath#toFile()
867      */

868     public File JavaDoc toFile() {
869         return new File JavaDoc(toOSString());
870     }
871
872     /* (Intentionally not included in javadoc)
873      * @see IPath#toOSString()
874      */

875     public String JavaDoc toOSString() {
876         //Note that this method is identical to toString except
877
//it uses the OS file separator instead of the path separator
878
int resultSize = computeLength();
879         if (resultSize <= 0)
880             return EMPTY_STRING;
881         char FILE_SEPARATOR = File.separatorChar;
882         char[] result = new char[resultSize];
883         int offset = 0;
884         if (device != null) {
885             int size = device.length();
886             device.getChars(0, size, result, offset);
887             offset += size;
888         }
889         if ((separators & HAS_LEADING) != 0)
890             result[offset++] = FILE_SEPARATOR;
891         if ((separators & IS_UNC) != 0)
892             result[offset++] = FILE_SEPARATOR;
893         int len = segments.length - 1;
894         if (len >= 0) {
895             //append all but the last segment, with separators
896
for (int i = 0; i < len; i++) {
897                 int size = segments[i].length();
898                 segments[i].getChars(0, size, result, offset);
899                 offset += size;
900                 result[offset++] = FILE_SEPARATOR;
901             }
902             //append the last segment
903
int size = segments[len].length();
904             segments[len].getChars(0, size, result, offset);
905             offset += size;
906         }
907         if ((separators & HAS_TRAILING) != 0)
908             result[offset++] = FILE_SEPARATOR;
909         return new String JavaDoc(result);
910     }
911
912     /* (Intentionally not included in javadoc)
913      * @see IPath#toPortableString()
914      */

915     public String JavaDoc toPortableString() {
916         int resultSize = computeLength();
917         if (resultSize <= 0)
918             return EMPTY_STRING;
919         StringBuffer JavaDoc result = new StringBuffer JavaDoc(resultSize);
920         if (device != null)
921             result.append(device);
922         if ((separators & HAS_LEADING) != 0)
923             result.append(SEPARATOR);
924         if ((separators & IS_UNC) != 0)
925             result.append(SEPARATOR);
926         int len = segments.length;
927         //append all segments with separators
928
for (int i = 0; i < len; i++) {
929             if (segments[i].indexOf(DEVICE_SEPARATOR) >= 0)
930                 encodeSegment(segments[i], result);
931             else
932                 result.append(segments[i]);
933             if (i < len-1 || (separators & HAS_TRAILING) != 0)
934                 result.append(SEPARATOR);
935         }
936         return result.toString();
937     }
938
939     /* (Intentionally not included in javadoc)
940      * @see IPath#toString()
941      */

942     public String JavaDoc toString() {
943         int resultSize = computeLength();
944         if (resultSize <= 0)
945             return EMPTY_STRING;
946         char[] result = new char[resultSize];
947         int offset = 0;
948         if (device != null) {
949             int size = device.length();
950             device.getChars(0, size, result, offset);
951             offset += size;
952         }
953         if ((separators & HAS_LEADING) != 0)
954             result[offset++] = SEPARATOR;
955         if ((separators & IS_UNC) != 0)
956             result[offset++] = SEPARATOR;
957         int len = segments.length - 1;
958         if (len >= 0) {
959             //append all but the last segment, with separators
960
for (int i = 0; i < len; i++) {
961                 int size = segments[i].length();
962                 segments[i].getChars(0, size, result, offset);
963                 offset += size;
964                 result[offset++] = SEPARATOR;
965             }
966             //append the last segment
967
int size = segments[len].length();
968             segments[len].getChars(0, size, result, offset);
969             offset += size;
970         }
971         if ((separators & HAS_TRAILING) != 0)
972             result[offset++] = SEPARATOR;
973         return new String JavaDoc(result);
974     }
975
976     /* (Intentionally not included in javadoc)
977      * @see IPath#uptoSegment(int)
978      */

979     public IPath uptoSegment(int count) {
980         if (count == 0)
981             return new Path(device, NO_SEGMENTS, separators & (HAS_LEADING | IS_UNC));
982         if (count >= segments.length)
983             return this;
984         Assert.isTrue(count > 0, "Invalid parameter to Path.uptoSegment"); //$NON-NLS-1$
985
String JavaDoc[] newSegments = new String JavaDoc[count];
986         System.arraycopy(segments, 0, newSegments, 0, count);
987         return new Path(device, newSegments, separators);
988     }
989 }
Popular Tags