Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Side by Side Diff: utils/pub/path.dart

Issue 11512011: Handle relative paths where the trailing directory has an extension. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// A comprehensive, cross-platform path manipulation library. 5 /// A comprehensive, cross-platform path manipulation library.
6 library path; 6 library path;
7 7
8 import 'dart:io' as io; 8 import 'dart:io' as io;
9 9
10 /// An internal builder for the current OS so we can provide a straight 10 /// An internal builder for the current OS so we can provide a straight
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 pathParsed.root = ''; 305 pathParsed.root = '';
306 pathParsed.removeTrailingSeparator(); 306 pathParsed.removeTrailingSeparator();
307 307
308 return pathParsed.toString(); 308 return pathParsed.toString();
309 } 309 }
310 310
311 /// Removes a trailing extension from the last part of [path]. 311 /// Removes a trailing extension from the last part of [path].
312 /// 312 ///
313 /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' 313 /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
314 String withoutExtension(String path) { 314 String withoutExtension(String path) {
315 var lastSeparator = path.lastIndexOf(separator); 315 var parsed = _parse(path);
316 var lastDot = path.lastIndexOf('.'); 316 if (parsed.hasTrailingSeparator) return parsed.toString();
nweiz 2012/12/11 02:09:42 I really don't like changing behavior based on whe
Bob Nystrom 2012/12/11 02:24:59 This is how Python works. I think it would be conf
nweiz 2012/12/11 02:34:46 But not Ruby :).
Bob Nystrom 2012/12/11 02:47:01 I'm more interested here in what dirname("foo/bar/
317 317
318 // Ignore '.' in anything but the last component. 318 if (!parsed.parts.isEmpty) {
319 if (lastSeparator != -1 && lastDot <= lastSeparator + 1) lastDot = -1; 319 var split = parsed._splitExtension();
320 parsed.parts[parsed.parts.length - 1] = split[0];
nweiz 2012/12/11 02:09:42 Calling a private method here seems like a code sm
Bob Nystrom 2012/12/11 02:24:59 Done, though I don't think there's anything wrong
321 }
320 322
321 if (lastDot <= 0) return path; 323 return parsed.toString();
322 return path.substring(0, lastDot);
323 } 324 }
324 325
325 _ParsedPath _parse(String path) { 326 _ParsedPath _parse(String path) {
326 var before = path; 327 var before = path;
327 328
328 // Remove the root prefix, if any. 329 // Remove the root prefix, if any.
329 var root = style.getRoot(path); 330 var root = style.getRoot(path);
330 if (root != null) path = path.substring(root.length); 331 if (root != null) path = path.substring(root.length);
331 332
332 // Split the parts on path separators. 333 // Split the parts on path separators.
333 var parts = []; 334 var parts = [];
334 var separators = []; 335 var separators = [];
335 var start = 0; 336 var start = 0;
336 for (var match in style.separatorPattern.allMatches(path)) { 337 for (var match in style.separatorPattern.allMatches(path)) {
337 parts.add(path.substring(start, match.start)); 338 parts.add(path.substring(start, match.start));
338 separators.add(match[0]); 339 separators.add(match[0]);
339 start = match.end; 340 start = match.end;
340 } 341 }
341 342
342 // Add the final part, if any. 343 // Add the final part, if any.
343 if (start < path.length) { 344 if (start < path.length) {
344 parts.add(path.substring(start)); 345 parts.add(path.substring(start));
345 separators.add(''); 346 separators.add('');
346 } 347 }
347 348
348 // Separate out the file extension. 349 return new _ParsedPath(style, root, parts, separators);
349 var extension = '';
350 if (parts.length > 0) {
351 var file = parts.last;
352 if (file != '..') {
353 var lastDot = file.lastIndexOf('.');
354
355 // If there is a dot (and it's not the first character, like '.bashrc').
356 if (lastDot > 0) {
357 parts[parts.length - 1] = file.substring(0, lastDot);
358 extension = file.substring(lastDot);
359 }
360 }
361 }
362
363 return new _ParsedPath(style, root, parts, separators, extension);
364 } 350 }
365 } 351 }
366 352
367 /// An enum type describing a "flavor" of path. 353 /// An enum type describing a "flavor" of path.
368 class Style { 354 class Style {
369 /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths 355 /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths
370 /// start with "/". Used by UNIX, Linux, Mac OS X, and others. 356 /// start with "/". Used by UNIX, Linux, Mac OS X, and others.
371 static final posix = new Style._('posix', '/', '/', '/'); 357 static final posix = new Style._('posix', '/', '/', '/');
372 358
373 /// Windows paths use "\" (backslash) as separators. Absolute paths start with 359 /// Windows paths use "\" (backslash) as separators. Absolute paths start with
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
412 /// The [Style] that was used to parse this path. 398 /// The [Style] that was used to parse this path.
413 Style style; 399 Style style;
414 400
415 /// The absolute root portion of the path, or `null` if the path is relative. 401 /// The absolute root portion of the path, or `null` if the path is relative.
416 /// On POSIX systems, this will be `null` or "/". On Windows, it can be 402 /// On POSIX systems, this will be `null` or "/". On Windows, it can be
417 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive 403 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive
418 /// letters. 404 /// letters.
419 String root; 405 String root;
420 406
421 /// The path-separated parts of the path. All but the last will be 407 /// The path-separated parts of the path. All but the last will be
422 /// directories. The last could be a directory, or could be the file name 408 /// directories.
423 /// without its extension.
424 List<String> parts; 409 List<String> parts;
425 410
426 /// The path separators following each part. The last one will be an empty 411 /// The path separators following each part. The last one will be an empty
427 /// string unless the path ends with a trailing separator. 412 /// string unless the path ends with a trailing separator.
428 List<String> separators; 413 List<String> separators;
429 414
430 /// The file's extension, or "" if it doesn't have one. 415 /// The file extension of the last part, or "" if it doesn't have one.
431 String extension; 416 String get extension => _splitExtension()[1];
432 417
433 /// `true` if the path ends with a trailing separator. 418 /// `true` if the path ends with a trailing separator.
434 bool get hasTrailingSeparator { 419 bool get hasTrailingSeparator {
435 if (separators.length == 0) return false; 420 if (separators.length == 0) return false;
436 return separators[separators.length - 1] != ''; 421 return separators[separators.length - 1] != '';
437 } 422 }
438 423
439 /// `true` if this is an absolute path. 424 /// `true` if this is an absolute path.
440 bool get isAbsolute => root != null; 425 bool get isAbsolute => root != null;
441 426
442 _ParsedPath(this.style, this.root, this.parts, this.separators, 427 _ParsedPath(this.style, this.root, this.parts, this.separators);
443 this.extension);
444 428
445 String get filename { 429 String get filename {
446 if (parts.length == 0) return extension; 430 if (parts.isEmpty) return '';
447 if (hasTrailingSeparator) return '';
448 return '${parts.last}$extension';
449 }
450
451 String get filenameWithoutExtension {
452 if (parts.length == 0) return '';
453 if (hasTrailingSeparator) return ''; 431 if (hasTrailingSeparator) return '';
454 return parts.last; 432 return parts.last;
455 } 433 }
456 434
435 String get filenameWithoutExtension => _splitExtension()[0];
436
457 void removeTrailingSeparator() { 437 void removeTrailingSeparator() {
458 if (separators.length > 0) { 438 if (separators.length > 0) {
459 separators[separators.length - 1] = ''; 439 separators[separators.length - 1] = '';
460 } 440 }
461 } 441 }
462 442
463 void normalize() { 443 void normalize() {
464 // Handle '.', '..', and empty parts. 444 // Handle '.', '..', and empty parts.
465 var leadingDoubles = 0; 445 var leadingDoubles = 0;
466 var newParts = []; 446 var newParts = [];
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 separators = newSeparators; 478 separators = newSeparators;
499 479
500 removeTrailingSeparator(); 480 removeTrailingSeparator();
501 } 481 }
502 482
503 String toString() { 483 String toString() {
504 var builder = new StringBuffer(); 484 var builder = new StringBuffer();
505 if (root != null) builder.add(root); 485 if (root != null) builder.add(root);
506 for (var i = 0; i < parts.length; i++) { 486 for (var i = 0; i < parts.length; i++) {
507 builder.add(parts[i]); 487 builder.add(parts[i]);
508 if (extension != null && i == parts.length - 1) builder.add(extension);
509 builder.add(separators[i]); 488 builder.add(separators[i]);
510 } 489 }
511 490
512 return builder.toString(); 491 return builder.toString();
513 } 492 }
493
494 /// Splits the last part of the path into a two-element list. The first is
495 /// the name of the file without any extension. The second is the extension
496 /// or "" if it has none.
497 List<String> _splitExtension() {
498 if (parts.isEmpty) return ['', ''];
499 if (hasTrailingSeparator) return ['', ''];
500
501 var file = parts.last;
502 if (file == '..') return ['..', ''];
503
504 var lastDot = file.lastIndexOf('.');
505
506 // If there is a dot (and it's not the first character, like '.bashrc').
nweiz 2012/12/11 02:09:42 This looks like it's supposed to go a couple lines
Bob Nystrom 2012/12/11 02:24:59 Reworded the comment.
507 if (lastDot <= 0) return [file, ''];
508
509 parts[parts.length - 1] = file.substring(0, lastDot);
nweiz 2012/12/11 02:09:42 Why is this useful? Doesn't this mean that calling
Bob Nystrom 2012/12/11 02:24:59 Eek! Copy/paste bug. Removed.
510 return [file.substring(0, lastDot), file.substring(lastDot)];
511 }
514 } 512 }
OLDNEW
« no previous file with comments | « no previous file | utils/tests/pub/path/path_posix_test.dart » ('j') | utils/tests/pub/path/path_windows_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698