Mercurial > projects > dwt2
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 } |