comparison org.eclipse.equinox.common/src/org/eclipse/core/runtime/Path.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children dccb717aa902
comparison
equal deleted inserted replaced
11:43904fec5dca 12:bc29606a740c
1 /*******************************************************************************
2 * Copyright (c) 2000, 2008 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 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module org.eclipse.core.runtime.Path;
14
15 import tango.io.FilePath;
16 static import tango.io.Path;
17 import org.eclipse.core.runtime.IPath;
18 import org.eclipse.core.runtime.Assert;
19
20 import java.lang.all;
21
22 import tango.io.model.IFile;
23
24
25 static import tango.text.Text;
26 alias tango.text.Text.Text!(char) StringBuffer;
27
28 /**
29 * The standard implementation of the <code>IPath</code> interface.
30 * Paths are always maintained in canonicalized form. That is, parent
31 * references (i.e., <code>../../</code>) and duplicate separators are
32 * resolved. For example,
33 * <pre> new Path("/a/b").append("../foo/bar")</pre>
34 * will yield the path
35 * <pre> /a/foo/bar</pre>
36 * <p>
37 * This class can be used without OSGi running.
38 * </p><p>
39 * This class is not intended to be subclassed by clients but
40 * may be instantiated.
41 * </p>
42 * @see IPath
43 * @noextend This class is not intended to be subclassed by clients.
44 */
45 public class Path : IPath, Cloneable {
46 /** masks for separator values */
47 private static const int HAS_LEADING = 1;
48 private static const int IS_UNC = 2;
49 private static const int HAS_TRAILING = 4;
50
51 private static const int ALL_SEPARATORS = HAS_LEADING | IS_UNC | HAS_TRAILING;
52
53 /** Constant empty string value. */
54 private static const String EMPTY_STRING = ""; //$NON-NLS-1$
55
56 /** Constant value indicating no segments */
57 private static const String[] NO_SEGMENTS = null;
58
59 /** Constant value containing the empty path with no device. */
60 public static const Path EMPTY;
61
62 /** Mask for all bits that are involved in the hash code */
63 private static const int HASH_MASK = ~HAS_TRAILING;
64
65 /** Constant root path string (<code>"/"</code>). */
66 private static const String ROOT_STRING = "/"; //$NON-NLS-1$
67
68 /** Constant value containing the root path with no device. */
69 public static const Path ROOT;
70
71 /** Constant value indicating if the current platform is Windows */
72 version(Windows){
73 private static const bool WINDOWS = true;
74 }
75 else {
76 private static const bool WINDOWS = false;
77 }
78
79 static this(){
80 EMPTY = new Path(EMPTY_STRING);
81 ROOT = new Path(ROOT_STRING);
82 }
83
84 /** The device id string. May be null if there is no device. */
85 private String device = null;
86
87 //Private implementation note: the segments and separators
88 //arrays are never modified, so that they can be shared between
89 //path instances
90
91 /** The path segments */
92 private String[] segments_;
93
94 /** flags indicating separators (has leading, is UNC, has trailing) */
95 private int separators;
96
97 /**
98 * Constructs a new path from the given string path.
99 * The string path must represent a valid file system path
100 * on the local file system.
101 * The path is canonicalized and double slashes are removed
102 * except at the beginning. (to handle UNC paths). All forward
103 * slashes ('/') are treated as segment delimiters, and any
104 * segment and device delimiters for the local file system are
105 * also respected.
106 *
107 * @param pathString the portable string path
108 * @see IPath#toPortableString()
109 * @since 3.1
110 */
111 public static IPath fromOSString(String pathString) {
112 return new Path(pathString);
113 }
114
115 /**
116 * Constructs a new path from the given path string.
117 * The path string must have been produced by a previous
118 * call to <code>IPath.toPortableString</code>.
119 *
120 * @param pathString the portable path string
121 * @see IPath#toPortableString()
122 * @since 3.1
123 */
124 public static IPath fromPortableString(String pathString) {
125 int firstMatch = pathString.indexOf(DEVICE_SEPARATOR) + 1;
126 //no extra work required if no device characters
127 if (firstMatch <= 0)
128 return (new Path()).initialize(null, pathString);
129 //if we find a single colon, then the path has a device
130 String devicePart = null;
131 int pathLength = pathString.length;
132 if (firstMatch is pathLength || pathString.charAt(firstMatch) !is DEVICE_SEPARATOR) {
133 devicePart = pathString.substring(0, firstMatch);
134 pathString = pathString.substring(firstMatch, pathLength);
135 }
136 //optimize for no colon literals
137 if (pathString.indexOf(DEVICE_SEPARATOR) is -1)
138 return (new Path()).initialize(devicePart, pathString);
139 //contract colon literals
140 char[] chars = pathString/+.toCharArray()+/;
141 int readOffset = 0, writeOffset = 0, length = chars.length;
142 while (readOffset < length) {
143 if (chars[readOffset] is DEVICE_SEPARATOR)
144 if (++readOffset >= length)
145 break;
146 chars[writeOffset++] = chars[readOffset++];
147 }
148 return (new Path()).initialize(devicePart, chars[ 0 .. writeOffset] );
149 }
150
151 /* (Intentionally not included in javadoc)
152 * Private constructor.
153 */
154 private this() {
155 // not allowed
156 }
157
158 /**
159 * Constructs a new path from the given string path.
160 * The string path must represent a valid file system path
161 * on the local file system.
162 * The path is canonicalized and double slashes are removed
163 * except at the beginning. (to handle UNC paths). All forward
164 * slashes ('/') are treated as segment delimiters, and any
165 * segment and device delimiters for the local file system are
166 * also respected (such as colon (':') and backslash ('\') on some file systems).
167 *
168 * @param fullPath the string path
169 * @see #isValidPath(String)
170 */
171 public this(String fullPath) {
172 String devicePart = null;
173 if (WINDOWS) {
174 //convert backslash to forward slash
175 fullPath = fullPath.indexOf('\\') is -1 ? fullPath : fullPath.replace('\\', SEPARATOR);
176 //extract device
177 int i = fullPath.indexOf(DEVICE_SEPARATOR);
178 if (i !is -1) {
179 //remove leading slash from device part to handle output of URL.getFile()
180 int start = fullPath.charAt(0) is SEPARATOR ? 1 : 0;
181 devicePart = fullPath.substring(start, i + 1);
182 fullPath = fullPath.substring(i + 1, fullPath.length);
183 }
184 }
185 initialize(devicePart, fullPath);
186 }
187
188 /**
189 * Constructs a new path from the given device id and string path.
190 * The given string path must be valid.
191 * The path is canonicalized and double slashes are removed except
192 * at the beginning (to handle UNC paths). All forward
193 * slashes ('/') are treated as segment delimiters, and any
194 * segment delimiters for the local file system are
195 * also respected (such as backslash ('\') on some file systems).
196 *
197 * @param device the device id
198 * @param path the string path
199 * @see #isValidPath(String)
200 * @see #setDevice(String)
201 */
202 public this(String device, String path) {
203 if (WINDOWS) {
204 //convert backslash to forward slash
205 path = path.indexOf('\\') is -1 ? path : path.replace('\\', SEPARATOR);
206 }
207 initialize(device, path);
208 }
209
210 /* (Intentionally not included in javadoc)
211 * Private constructor.
212 */
213 private this(String device, String[] segments_, int _separators) {
214 // no segment validations are done for performance reasons
215 this.segments_ = segments_;
216 this.device = device;
217 //hash code is cached in all but the bottom three bits of the separators field
218 this.separators = (computeHashCode() << 3) | (_separators & ALL_SEPARATORS);
219 }
220
221 /* (Intentionally not included in javadoc)
222 * @see IPath#addFileExtension
223 */
224 public IPath addFileExtension(String extension) {
225 if (isRoot() || isEmpty() || hasTrailingSeparator())
226 return this;
227 int len = segments_.length;
228 String[] newSegments = new String[len];
229 System.arraycopy(segments_, 0, newSegments, 0, len - 1);
230 newSegments[len - 1] = segments_[len - 1] ~ '.' ~ extension;
231 return new Path(device, newSegments, separators);
232 }
233
234 /* (Intentionally not included in javadoc)
235 * @see IPath#addTrailingSeparator
236 */
237 public IPath addTrailingSeparator() {
238 if (hasTrailingSeparator() || isRoot()) {
239 return this;
240 }
241 //XXX workaround, see 1GIGQ9V
242 if (isEmpty()) {
243 return new Path(device, segments_, HAS_LEADING);
244 }
245 return new Path(device, segments_, separators | HAS_TRAILING);
246 }
247
248 /* (Intentionally not included in javadoc)
249 * @see IPath#append(IPath)
250 */
251 public IPath append(IPath tail) {
252 //optimize some easy cases
253 if (tail is null || tail.segmentCount() is 0)
254 return this;
255 //these call chains look expensive, but in most cases they are no-ops
256 if (this.isEmpty())
257 return tail.setDevice(device).makeRelative().makeUNC(isUNC());
258 if (this.isRoot())
259 return tail.setDevice(device).makeAbsolute().makeUNC(isUNC());
260
261 //concatenate the two segment arrays
262 int myLen = segments_.length;
263 int tailLen = tail.segmentCount();
264 String[] newSegments = new String[myLen + tailLen];
265 System.arraycopy(segments_, 0, newSegments, 0, myLen);
266 for (int i = 0; i < tailLen; i++) {
267 newSegments[myLen + i] = tail.segment(i);
268 }
269 //use my leading separators and the tail's trailing separator
270 Path result = new Path(device, newSegments, (separators & (HAS_LEADING | IS_UNC)) | (tail.hasTrailingSeparator() ? HAS_TRAILING : 0));
271 String tailFirstSegment = newSegments[myLen];
272 if (tailFirstSegment.equals("..") || tailFirstSegment.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$
273 result.canonicalize();
274 }
275 return result;
276 }
277
278 /* (Intentionally not included in javadoc)
279 * @see IPath#append(java.lang.String)
280 */
281 public IPath append(String tail) {
282 //optimize addition of a single segment
283 if (tail.indexOf(SEPARATOR) is -1 && tail.indexOf("\\") is -1 && tail.indexOf(DEVICE_SEPARATOR) is -1) { //$NON-NLS-1$
284 int tailLength = tail.length;
285 if (tailLength < 3) {
286 //some special cases
287 if (tailLength is 0 || ".".equals(tail)) { //$NON-NLS-1$
288 return this;
289 }
290 if ("..".equals(tail)) //$NON-NLS-1$
291 return removeLastSegments(1);
292 }
293 //just add the segment
294 int myLen = segments_.length;
295 String[] newSegments = new String[myLen + 1];
296 System.arraycopy(segments_, 0, newSegments, 0, myLen);
297 newSegments[myLen] = tail;
298 return new Path(device, newSegments, separators & ~HAS_TRAILING);
299 }
300 //go with easy implementation
301 return append(new Path(tail));
302 }
303
304 /**
305 * Destructively converts this path to its canonical form.
306 * <p>
307 * In its canonical form, a path does not have any
308 * "." segments, and parent references ("..") are collapsed
309 * where possible.
310 * </p>
311 * @return true if the path was modified, and false otherwise.
312 */
313 private bool canonicalize() {
314 //look for segments that need canonicalizing
315 for (int i = 0, max = segments_.length; i < max; i++) {
316 String segment = segments_[i];
317 if (segment.charAt(0) is '.' && (segment.equals("..") || segment.equals("."))) { //$NON-NLS-1$ //$NON-NLS-2$
318 //path needs to be canonicalized
319 collapseParentReferences();
320 //paths of length 0 have no trailing separator
321 if (segments_.length is 0)
322 separators &= (HAS_LEADING | IS_UNC);
323 //recompute hash because canonicalize affects hash
324 separators = (separators & ALL_SEPARATORS) | (computeHashCode() << 3);
325 return true;
326 }
327 }
328 return false;
329 }
330
331 /* (Intentionally not included in javadoc)
332 * Clones this object.
333 */
334 public Path clone() {
335 return new Path(device, segments_, separators);
336 }
337
338 /**
339 * Destructively removes all occurrences of ".." segments from this path.
340 */
341 private void collapseParentReferences() {
342 int segmentCount = segments_.length;
343 String[] stack = new String[segmentCount];
344 int stackPointer = 0;
345 for (int i = 0; i < segmentCount; i++) {
346 String segment = segments_[i];
347 if (segment.equals("..")) { //$NON-NLS-1$
348 if (stackPointer is 0) {
349 // if the stack is empty we are going out of our scope
350 // so we need to accumulate segments. But only if the original
351 // path is relative. If it is absolute then we can't go any higher than
352 // root so simply toss the .. references.
353 if (!isAbsolute())
354 stack[stackPointer++] = segment; //stack push
355 } else {
356 // if the top is '..' then we are accumulating segments so don't pop
357 if ("..".equals(stack[stackPointer - 1])) //$NON-NLS-1$
358 stack[stackPointer++] = ".."; //$NON-NLS-1$
359 else
360 stackPointer--;
361 //stack pop
362 }
363 //collapse current references
364 } else if (!segment.equals(".") || segmentCount is 1) //$NON-NLS-1$
365 stack[stackPointer++] = segment; //stack push
366 }
367 //if the number of segments hasn't changed, then no modification needed
368 if (stackPointer is segmentCount)
369 return;
370 //build the new segment array backwards by popping the stack
371 String[] newSegments = new String[stackPointer];
372 System.arraycopy(stack, 0, newSegments, 0, stackPointer);
373 this.segments_ = newSegments;
374 }
375
376 /**
377 * Removes duplicate slashes from the given path, with the exception
378 * of leading double slash which represents a UNC path.
379 */
380 private String collapseSlashes(String path) {
381 int length = path.length;
382 // if the path is only 0, 1 or 2 chars long then it could not possibly have illegal
383 // duplicate slashes.
384 if (length < 3)
385 return path;
386 // check for an occurrence of // in the path. Start at index 1 to ensure we skip leading UNC //
387 // If there are no // then there is nothing to collapse so just return.
388 if (path.indexOf("//", 1) is -1) //$NON-NLS-1$
389 return path;
390 // We found an occurrence of // in the path so do the slow collapse.
391 char[] result = new char[path.length];
392 int count = 0;
393 bool hasPrevious = false;
394 char[] characters = path/+.toCharArray()+/;
395 for (int index = 0; index < characters.length; index++) {
396 char c = characters[index];
397 if (c is SEPARATOR) {
398 if (hasPrevious) {
399 // skip double slashes, except for beginning of UNC.
400 // note that a UNC path can't have a device.
401 if (device is null && index is 1) {
402 result[count] = c;
403 count++;
404 }
405 } else {
406 hasPrevious = true;
407 result[count] = c;
408 count++;
409 }
410 } else {
411 hasPrevious = false;
412 result[count] = c;
413 count++;
414 }
415 }
416 return result[ 0 .. count];
417 }
418
419 /* (Intentionally not included in javadoc)
420 * Computes the hash code for this object.
421 */
422 private int computeHashCode() {
423 int hash = device.length is 0 ? 17 : java.lang.all.toHash(device);
424 int segmentCount = segments_.length;
425 for (int i = 0; i < segmentCount; i++) {
426 //this function tends to given a fairly even distribution
427 hash = hash * 37 + java.lang.all.toHash(segments_[i]);
428 }
429 return hash;
430 }
431
432 /* (Intentionally not included in javadoc)
433 * Returns the size of the string that will be created by toString or toOSString.
434 */
435 private int computeLength() {
436 int length = 0;
437 if (device !is null)
438 length += device.length;
439 if ((separators & HAS_LEADING) !is 0)
440 length++;
441 if ((separators & IS_UNC) !is 0)
442 length++;
443 //add the segment lengths
444 int max = segments_.length;
445 if (max > 0) {
446 for (int i = 0; i < max; i++) {
447 length += segments_[i].length;
448 }
449 //add the separator lengths
450 length += max - 1;
451 }
452 if ((separators & HAS_TRAILING) !is 0)
453 length++;
454 return length;
455 }
456
457 /* (Intentionally not included in javadoc)
458 * Returns the number of segments in the given path
459 */
460 private int computeSegmentCount(String path) {
461 int len = path.length;
462 if (len is 0 || (len is 1 && path.charAt(0) is SEPARATOR)) {
463 return 0;
464 }
465 int count = 1;
466 int prev = -1;
467 int i;
468 while ((i = path.indexOf(SEPARATOR, prev + 1)) !is -1) {
469 if (i !is prev + 1 && i !is len) {
470 ++count;
471 }
472 prev = i;
473 }
474 if (path.charAt(len - 1) is SEPARATOR) {
475 --count;
476 }
477 return count;
478 }
479
480 /**
481 * Computes the segment array for the given canonicalized path.
482 */
483 private String[] computeSegments(String path) {
484 // performance sensitive --- avoid creating garbage
485 int segmentCount = computeSegmentCount(path);
486 if (segmentCount is 0)
487 return NO_SEGMENTS;
488 String[] newSegments = new String[segmentCount];
489 int len = path.length;
490 // check for initial slash
491 int firstPosition = (path.charAt(0) is SEPARATOR) ? 1 : 0;
492 // check for UNC
493 if (firstPosition is 1 && len > 1 && (path.charAt(1) is SEPARATOR))
494 firstPosition = 2;
495 int lastPosition = (path.charAt(len - 1) !is SEPARATOR) ? len - 1 : len - 2;
496 // for non-empty paths, the number of segments is
497 // the number of slashes plus 1, ignoring any leading
498 // and trailing slashes
499 int next = firstPosition;
500 for (int i = 0; i < segmentCount; i++) {
501 int start = next;
502 int end = path.indexOf(SEPARATOR, next);
503 if (end is -1) {
504 newSegments[i] = path.substring(start, lastPosition + 1);
505 } else {
506 newSegments[i] = path.substring(start, end);
507 }
508 next = end + 1;
509 }
510 return newSegments;
511 }
512
513 /**
514 * Returns the platform-neutral encoding of the given segment onto
515 * the given string buffer. This escapes literal colon characters with double colons.
516 */
517 private void encodeSegment(String string, StringBuffer buf) {
518 int len = string.length;
519 for (int i = 0; i < len; i++) {
520 char c = string.charAt(i);
521 buf.append(c);
522 if (c is DEVICE_SEPARATOR)
523 buf.append(DEVICE_SEPARATOR);
524 }
525 }
526
527 /* (Intentionally not included in javadoc)
528 * Compares objects for equality.
529 */
530 public override int opEquals(Object obj) {
531 if (this is obj)
532 return true;
533 if (!(cast(Path)obj))
534 return false;
535 Path target = cast(Path) obj;
536 //check leading separators and hash code
537 if ((separators & HASH_MASK) !is (target.separators & HASH_MASK))
538 return false;
539 String[] targetSegments = target.segments_;
540 int i = segments_.length;
541 //check segment count
542 if (i !is targetSegments.length)
543 return false;
544 //check segments in reverse order - later segments more likely to differ
545 while (--i >= 0)
546 if (!segments_[i].equals(targetSegments[i]))
547 return false;
548 //check device last (least likely to differ)
549 return device is target.device || (device !is null && device.equals(target.device));
550 }
551
552 /* (Intentionally not included in javadoc)
553 * @see IPath#getDevice
554 */
555 public String getDevice() {
556 return device;
557 }
558
559 /* (Intentionally not included in javadoc)
560 * @see IPath#getFileExtension
561 */
562 public String getFileExtension() {
563 if (hasTrailingSeparator()) {
564 return null;
565 }
566 String lastSegment = lastSegment();
567 if (lastSegment is null) {
568 return null;
569 }
570 int index = lastSegment.lastIndexOf('.');
571 if (index is -1) {
572 return null;
573 }
574 return lastSegment.substring(index + 1);
575 }
576
577 /* (Intentionally not included in javadoc)
578 * Computes the hash code for this object.
579 */
580 public override hash_t toHash() {
581 return separators & HASH_MASK;
582 }
583
584 /* (Intentionally not included in javadoc)
585 * @see IPath#hasTrailingSeparator2
586 */
587 public bool hasTrailingSeparator() {
588 return (separators & HAS_TRAILING) !is 0;
589 }
590
591 /*
592 * Initialize the current path with the given string.
593 */
594 private IPath initialize(String deviceString, String path) {
595 //Assert.isNotNull(path); // allow for SWT
596 this.device = deviceString;
597
598 path = collapseSlashes(path);
599 int len = path.length;
600
601 //compute the separators array
602 if (len < 2) {
603 if (len is 1 && path.charAt(0) is SEPARATOR) {
604 this.separators = HAS_LEADING;
605 } else {
606 this.separators = 0;
607 }
608 } else {
609 bool hasLeading = path.charAt(0) is SEPARATOR;
610 bool isUNC = hasLeading && path.charAt(1) is SEPARATOR;
611 //UNC path of length two has no trailing separator
612 bool hasTrailing = !(isUNC && len is 2) && path.charAt(len - 1) is SEPARATOR;
613 separators = hasLeading ? HAS_LEADING : 0;
614 if (isUNC)
615 separators |= IS_UNC;
616 if (hasTrailing)
617 separators |= HAS_TRAILING;
618 }
619 //compute segments and ensure canonical form
620 segments_ = computeSegments(path);
621 if (!canonicalize()) {
622 //compute hash now because canonicalize didn't need to do it
623 separators = (separators & ALL_SEPARATORS) | (computeHashCode() << 3);
624 }
625 return this;
626 }
627
628 /* (Intentionally not included in javadoc)
629 * @see IPath#isAbsolute
630 */
631 public bool isAbsolute() {
632 //it's absolute if it has a leading separator
633 return (separators & HAS_LEADING) !is 0;
634 }
635
636 /* (Intentionally not included in javadoc)
637 * @see IPath#isEmpty
638 */
639 public bool isEmpty() {
640 //true if no segments and no leading prefix
641 return segments_.length is 0 && ((separators & ALL_SEPARATORS) !is HAS_LEADING);
642
643 }
644
645 /* (Intentionally not included in javadoc)
646 * @see IPath#isPrefixOf
647 */
648 public bool isPrefixOf(IPath anotherPath) {
649 if (device is null) {
650 if (anotherPath.getDevice() !is null) {
651 return false;
652 }
653 } else {
654 if (!device.equalsIgnoreCase(anotherPath.getDevice())) {
655 return false;
656 }
657 }
658 if (isEmpty() || (isRoot() && anotherPath.isAbsolute())) {
659 return true;
660 }
661 int len = segments_.length;
662 if (len > anotherPath.segmentCount()) {
663 return false;
664 }
665 for (int i = 0; i < len; i++) {
666 if (!segments_[i].equals(anotherPath.segment(i)))
667 return false;
668 }
669 return true;
670 }
671
672 /* (Intentionally not included in javadoc)
673 * @see IPath#isRoot
674 */
675 public bool isRoot() {
676 //must have no segments, a leading separator, and not be a UNC path.
677 return this is ROOT || (segments_.length is 0 && ((separators & ALL_SEPARATORS) is HAS_LEADING));
678 }
679
680 /* (Intentionally not included in javadoc)
681 * @see IPath#isUNC
682 */
683 public bool isUNC() {
684 if (device !is null)
685 return false;
686 return (separators & IS_UNC) !is 0;
687 }
688
689 /* (Intentionally not included in javadoc)
690 * @see IPath#isValidPath(String)
691 */
692 public bool isValidPath(String path) {
693 Path test = new Path(path);
694 for (int i = 0, max = test.segmentCount(); i < max; i++)
695 if (!isValidSegment(test.segment(i)))
696 return false;
697 return true;
698 }
699
700 /* (Intentionally not included in javadoc)
701 * @see IPath#isValidSegment(String)
702 */
703 public bool isValidSegment(String segment) {
704 int size = segment.length;
705 if (size is 0)
706 return false;
707 for (int i = 0; i < size; i++) {
708 char c = segment.charAt(i);
709 if (c is '/')
710 return false;
711 if (WINDOWS && (c is '\\' || c is ':'))
712 return false;
713 }
714 return true;
715 }
716
717 /* (Intentionally not included in javadoc)
718 * @see IPath#lastSegment()
719 */
720 public String lastSegment() {
721 int len = segments_.length;
722 return len is 0 ? null : segments_[len - 1];
723 }
724
725 /* (Intentionally not included in javadoc)
726 * @see IPath#makeAbsolute()
727 */
728 public IPath makeAbsolute() {
729 if (isAbsolute()) {
730 return this;
731 }
732 Path result = new Path(device, segments_, separators | HAS_LEADING);
733 //may need canonicalizing if it has leading ".." or "." segments
734 if (result.segmentCount() > 0) {
735 String first = result.segment(0);
736 if (first.equals("..") || first.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$
737 result.canonicalize();
738 }
739 }
740 return result;
741 }
742
743 /* (Intentionally not included in javadoc)
744 * @see IPath#makeRelative()
745 */
746 public IPath makeRelative() {
747 if (!isAbsolute()) {
748 return this;
749 }
750 return new Path(device, segments_, separators & HAS_TRAILING);
751 }
752
753 /* (Intentionally not included in javadoc)
754 * @see IPath#makeUNC(bool)
755 */
756 public IPath makeUNC(bool toUNC) {
757 // if we are already in the right form then just return
758 if (!(toUNC ^ isUNC()))
759 return this;
760
761 int newSeparators = this.separators;
762 if (toUNC) {
763 newSeparators |= HAS_LEADING | IS_UNC;
764 } else {
765 //mask out the UNC bit
766 newSeparators &= HAS_LEADING | HAS_TRAILING;
767 }
768 return new Path(toUNC ? null : device, segments_, newSeparators);
769 }
770
771 /* (Intentionally not included in javadoc)
772 * @see IPath#matchingFirstSegments(IPath)
773 */
774 public int matchingFirstSegments(IPath anotherPath) {
775 Assert.isNotNull( cast(Object) anotherPath);
776 int anotherPathLen = anotherPath.segmentCount();
777 int max = Math.min(segments_.length, anotherPathLen);
778 int count = 0;
779 for (int i = 0; i < max; i++) {
780 if (!segments_[i].equals(anotherPath.segment(i))) {
781 return count;
782 }
783 count++;
784 }
785 return count;
786 }
787
788 /* (Intentionally not included in javadoc)
789 * @see IPath#removeFileExtension()
790 */
791 public IPath removeFileExtension() {
792 String extension = getFileExtension();
793 if (extension is null || extension.equals("")) { //$NON-NLS-1$
794 return this;
795 }
796 String lastSegment = lastSegment();
797 int index = lastSegment.lastIndexOf(extension) - 1;
798 return removeLastSegments(1).append(lastSegment.substring(0, index));
799 }
800
801 /* (Intentionally not included in javadoc)
802 * @see IPath#removeFirstSegments(int)
803 */
804 public IPath removeFirstSegments(int count) {
805 if (count is 0)
806 return this;
807 if (count >= segments_.length) {
808 return new Path(device, NO_SEGMENTS, 0);
809 }
810 Assert.isLegal(count > 0);
811 int newSize = segments_.length - count;
812 String[] newSegments = new String[newSize];
813 System.arraycopy(this.segments_, count, newSegments, 0, newSize);
814
815 //result is always a relative path
816 return new Path(device, newSegments, separators & HAS_TRAILING);
817 }
818
819 /* (Intentionally not included in javadoc)
820 * @see IPath#removeLastSegments(int)
821 */
822 public IPath removeLastSegments(int count) {
823 if (count is 0)
824 return this;
825 if (count >= segments_.length) {
826 //result will have no trailing separator
827 return new Path(device, NO_SEGMENTS, separators & (HAS_LEADING | IS_UNC));
828 }
829 Assert.isLegal(count > 0);
830 int newSize = segments_.length - count;
831 String[] newSegments = new String[newSize];
832 System.arraycopy(this.segments_, 0, newSegments, 0, newSize);
833 return new Path(device, newSegments, separators);
834 }
835
836 /* (Intentionally not included in javadoc)
837 * @see IPath#removeTrailingSeparator()
838 */
839 public IPath removeTrailingSeparator() {
840 if (!hasTrailingSeparator()) {
841 return this;
842 }
843 return new Path(device, segments_, separators & (HAS_LEADING | IS_UNC));
844 }
845
846 /* (Intentionally not included in javadoc)
847 * @see IPath#segment(int)
848 */
849 public String segment(int index) {
850 if (index >= segments_.length)
851 return null;
852 return segments_[index];
853 }
854
855 /* (Intentionally not included in javadoc)
856 * @see IPath#segmentCount()
857 */
858 public int segmentCount() {
859 return segments_.length;
860 }
861
862 /* (Intentionally not included in javadoc)
863 * @see IPath#segments()
864 */
865 public String[] segments() {
866 String[] segmentCopy = new String[](segments_.length);
867 System.arraycopy(segments_, 0, segmentCopy, 0, segments_.length);
868 return segmentCopy;
869 }
870
871 /* (Intentionally not included in javadoc)
872 * @see IPath#setDevice(String)
873 */
874 public IPath setDevice(String value) {
875 if (value !is null) {
876 Assert.isTrue(value.indexOf(IPath.DEVICE_SEPARATOR) is (value.length - 1), "Last character should be the device separator"); //$NON-NLS-1$
877 }
878 //return the receiver if the device is the same
879 if (value is device || (value !is null && value.equals(device)))
880 return this;
881
882 return new Path(value, segments_, separators);
883 }
884
885 /* (Intentionally not included in javadoc)
886 * @see IPath#toFile()
887 */
888 public FilePath toFile() {
889 return new FilePath(tango.io.Path.standard(toOSString()));
890 }
891
892 /* (Intentionally not included in javadoc)
893 * @see IPath#toOSString()
894 */
895 public String toOSString() {
896 //Note that this method is identical to toString except
897 //it uses the OS file separator instead of the path separator
898 int resultSize = computeLength();
899 if (resultSize <= 0)
900 return EMPTY_STRING;
901 char FILE_SEPARATOR = FileConst.PathSeparatorChar;
902 char[] result = new char[resultSize];
903 int offset = 0;
904 if (device !is null) {
905 int size = device.length;
906 device.getChars(0, size, result, offset);
907 offset += size;
908 }
909 if ((separators & HAS_LEADING) !is 0)
910 result[offset++] = FILE_SEPARATOR;
911 if ((separators & IS_UNC) !is 0)
912 result[offset++] = FILE_SEPARATOR;
913 int len = segments_.length - 1;
914 if (len >= 0) {
915 //append all but the last segment, with separators
916 for (int i = 0; i < len; i++) {
917 int size = segments_[i].length;
918 segments_[i].getChars(0, size, result, offset);
919 offset += size;
920 result[offset++] = FILE_SEPARATOR;
921 }
922 //append the last segment
923 int size = segments_[len].length;
924 segments_[len].getChars(0, size, result, offset);
925 offset += size;
926 }
927 if ((separators & HAS_TRAILING) !is 0)
928 result[offset++] = FILE_SEPARATOR;
929 return result;
930 }
931
932 /* (Intentionally not included in javadoc)
933 * @see IPath#toPortableString()
934 */
935 public String toPortableString() {
936 int resultSize = computeLength();
937 if (resultSize <= 0)
938 return EMPTY_STRING;
939 StringBuffer result = new StringBuffer(resultSize);
940 if (device !is null)
941 result.append(device);
942 if ((separators & HAS_LEADING) !is 0)
943 result.append(SEPARATOR);
944 if ((separators & IS_UNC) !is 0)
945 result.append(SEPARATOR);
946 int len = segments_.length;
947 //append all segments with separators
948 for (int i = 0; i < len; i++) {
949 if (segments_[i].indexOf(DEVICE_SEPARATOR) >= 0)
950 encodeSegment(segments_[i], result);
951 else
952 result.append(segments_[i]);
953 if (i < len - 1 || (separators & HAS_TRAILING) !is 0)
954 result.append(SEPARATOR);
955 }
956 return result.toString();
957 }
958
959 /* (Intentionally not included in javadoc)
960 * @see IPath#toString()
961 */
962 public override String toString() {
963 int resultSize = computeLength();
964 if (resultSize <= 0)
965 return EMPTY_STRING;
966 char[] result = new char[resultSize];
967 int offset = 0;
968 if (device !is null) {
969 int size = device.length;
970 device.getChars(0, size, result, offset);
971 offset += size;
972 }
973 if ((separators & HAS_LEADING) !is 0)
974 result[offset++] = SEPARATOR;
975 if ((separators & IS_UNC) !is 0)
976 result[offset++] = SEPARATOR;
977 int len = segments_.length - 1;
978 if (len >= 0) {
979 //append all but the last segment, with separators
980 for (int i = 0; i < len; i++) {
981 int size = segments_[i].length;
982 segments_[i].getChars(0, size, result, offset);
983 offset += size;
984 result[offset++] = SEPARATOR;
985 }
986 //append the last segment
987 int size = segments_[len].length;
988 segments_[len].getChars(0, size, result, offset);
989 offset += size;
990 }
991 if ((separators & HAS_TRAILING) !is 0)
992 result[offset++] = SEPARATOR;
993 return result;
994 }
995
996 /* (Intentionally not included in javadoc)
997 * @see IPath#uptoSegment(int)
998 */
999 public IPath uptoSegment(int count) {
1000 if (count is 0)
1001 return new Path(device, NO_SEGMENTS, separators & (HAS_LEADING | IS_UNC));
1002 if (count >= segments_.length)
1003 return this;
1004 Assert.isTrue(count > 0, "Invalid parameter to Path.uptoSegment"); //$NON-NLS-1$
1005 String[] newSegments = new String[count];
1006 System.arraycopy(segments_, 0, newSegments, 0, count);
1007 return new Path(device, newSegments, separators);
1008 }
1009 }